Split native Periscope (periscope/src) from inherited PinScope (periscope/dependency).
Keep validate.py and the finding engine as-is. AGPL LICENSE stays at the repo root. Docker overlays dependency then src. Do not delete the inherited tree.
This commit is contained in:
@@ -1,892 +0,0 @@
|
||||
# PHASE 0 — PinScope / Periscope forensic audit
|
||||
|
||||
**Status:** observational only. No source, license, git history, remotes, or fork relationship was modified as part of this audit.
|
||||
**Goal:** architectural independence of Periscope from PinScope — not deletion of every inherited line.
|
||||
**Does not implement Phase 1.** Disposition labels are inventory, not a rewrite plan.
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Audit date | 2026-09-20 |
|
||||
| GitHub repo | https://github.com/manvalan/periscope (`fork: true`) |
|
||||
| Declared parent / source | https://github.com/Faradworks/Pinscope |
|
||||
| Product snapshot (working tree described here) | `origin/cursor/pcb-review-plan-44dd` = `facbaa23058be51de3eb2551e074ada270b394d5` |
|
||||
| GitHub default branch | `origin/main` = `ab1c5b081cefb6445b053c170203d6ffe0a88d1b` |
|
||||
| Upstream `main` | `upstream/main` = `b26ad3509f4a19878fb7050aad3da9eacba8d914` (fetched locally for comparison only; **GitHub remotes of the fork were not changed**) |
|
||||
| Merge-base (all three tips) | `b26ad35` — upstream HEAD *is* the fork point |
|
||||
| This document | `docs/development/PINSCOPE_AUDIT.md` (only file this phase is allowed to add) |
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive summary
|
||||
|
||||
Periscope is a **live GitHub fork** of Faradworks/Pinscope (AGPL-3.0). Upstream has **five** commits and has not moved since 2026-07-17. GitHub `main` on the fork is only **three** commits ahead (Docker / OSS / README). The **product** (DeepSeek, rebrand, PCB exam, finding engine, v2.32.1) lives on `cursor/pcb-review-plan-44dd`: **92** commits ahead of upstream, **89** ahead of `origin/main`, **not merged** to the default branch. GitHub lists **no pull requests**.
|
||||
|
||||
On the product snapshot (`facbaa2`, 404 files, 123 544 text lines):
|
||||
|
||||
| Class | Files | File % | Lines | Line % |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| UPSTREAM (identical blob) | 125 | 30.9% | 45 211 | 36.6% |
|
||||
| DERIVED (measurable overlap with an upstream path or rename) | 127 | 31.4% | 52 455 | 42.5% |
|
||||
| REPLACEMENT (same path, Jaccard < 0.20) | 4 | 1.0% | 359 | 0.3% |
|
||||
| NEW (no upstream match at the thresholds below) | 139 | 34.4% | 24 437 | 19.8% |
|
||||
| THIRD_PARTY (`vendor/` except empty `__init__.py` mis-tagged — see §3) | 9 | 2.2% | 1 082 | 0.9% |
|
||||
| UNKNOWN | 0 | — | — | — |
|
||||
|
||||
**PinScope-lineage share (UPSTREAM + DERIVED + REPLACEMENT):** 63.4% of files, 79.4% of lines.
|
||||
**New / vendored share (NEW + THIRD_PARTY):** 36.6% of files, 20.7% of lines.
|
||||
|
||||
Architectural independence is **not** blocked by leftover strings. It **is** blocked by: (1) the still-PinScope pipeline (`periscopex` ← `pinscopex`: graph, parsers, `validate.py`, extraction, Next.js report shell); (2) Faradworks legal/metadata identity still shipping in the UI; (3) GitHub `main` not carrying the product; (4) Clerk / Anthropic seams still in the runtime graph; (5) vendored ImpedenceFinder with **no license file and GitHub `license: null`**.
|
||||
|
||||
Most important to replace **first** (identity and branch topology), not the finding engine or PCB job: Faradworks terms/privacy/contact/metadata; then isolate Clerk and Anthropic; keep parsers/graph as KEEP until a later phase.
|
||||
|
||||
---
|
||||
|
||||
## 2. Method and non-goals
|
||||
|
||||
**Compared trees**
|
||||
|
||||
- Upstream: `git fetch` of `https://github.com/Faradworks/Pinscope.git` into a **local** remote named `upstream` on a disposable clone. This audit did not `git remote set-url`, force-push, rewrite history, or detach the GitHub fork.
|
||||
- Product: `origin/cursor/pcb-review-plan-44dd` (also compared `origin/main` and `origin/cursor/deepseek-71c5`).
|
||||
- File identity: `git ls-tree` blob SHAs.
|
||||
- Text overlap: line-**set** Jaccard on `git show` text. Thresholds (fixed, not guessed): identical SHA → UPSTREAM; same path and Jaccard ≥ 0.20 **or** ≥ 20 shared lines → DERIVED; same path and below that → REPLACEMENT; path only in HEAD with identical SHA to an unmatched upstream blob, or Jaccard ≥ 0.15 against a basename / `pinscopex`→`periscopex` map → DERIVED/REPLACEMENT with `renamed_from`; `vendor/` → THIRD_PARTY; else NEW.
|
||||
- Licenses: root `LICENSE` blob vs upstream; npm `license` field on **every** `frontend/package-lock.json` `packages` entry (827 entries, 0 missing); PyPI JSON `info.license` / `license_expression` / License classifiers for each `backend/requirements.txt` name; ImpedenceFinder GitHub API + tree + `pyproject.toml` + `LICENSE` URL. **No license was inferred from a package name.**
|
||||
- Strings: ripgrep for `Pinscope`, `PinScope`, `pinscope`, `Faradworks`, `faradworks`, `SPDX`, `copyright` (lockfile excluded from hit lists). Matches were classified, not deleted.
|
||||
|
||||
**Classifier caveat (empty blobs):** git empty-file SHA `e69de29` is shared by `backend/__init__.py` (UPSTREAM) and three HEAD empty files. The rename heuristic tagged `backend/periscopex/__init__.py`, `tests/impedancefinder/__init__.py`, and `vendor/impedancefinder/__init__.py` as DERIVED-from-`backend/__init__.py`. That is **not** a meaningful derivation. Corrected intent: package markers NEW / THIRD_PARTY. Counts in §1 are the **raw** classifier (reproducible). Adjusted: DERIVED 124, NEW 141, THIRD_PARTY 10.
|
||||
|
||||
**Non-goals:** no refactors, no PCB/finding-engine edits, no license changes, no history rewrite, no “delete every PinScope line.”
|
||||
|
||||
---
|
||||
|
||||
## 3. Git history map
|
||||
|
||||
### 3.1 Exact upstream and remotes (GitHub, not this clone)
|
||||
|
||||
From `GET https://api.github.com/repos/manvalan/periscope` (2026-09-20):
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| `full_name` | `manvalan/periscope` |
|
||||
| `fork` | `true` |
|
||||
| `parent.full_name` / `source.full_name` | `Faradworks/Pinscope` |
|
||||
| `default_branch` | `main` |
|
||||
| `license.spdx_id` | `AGPL-3.0` (same as parent) |
|
||||
| `created_at` | 2026-07-17T16:23:01Z |
|
||||
| Parent `pushed_at` | 2026-07-17T07:34:32Z |
|
||||
| Fork `pushed_at` | 2026-09-20T07:13:57Z |
|
||||
| Open / listed PRs | `[]` (empty) |
|
||||
|
||||
GitHub still records a **fork of Faradworks/Pinscope**. This audit did not detach it.
|
||||
|
||||
Visible `origin` branches: `main`, `cursor/deepseek-71c5`, `cursor/pcb-review-plan-44dd`. Upstream GitHub has only `main`.
|
||||
|
||||
### 3.2 Upstream commit list (complete — 5 commits)
|
||||
|
||||
| SHA | Date | Author | Subject |
|
||||
| --- | --- | --- | --- |
|
||||
| `6672d2be5744239bd6edfee8abc2ac43d3705f93` | 2026-07-17 | Siddharth Kothari `<Sid@faradworks.com>` | Pinscope open-source core (root commit; extracted from cloud codebase; auth/billing in private gateway) |
|
||||
| `6e83ef9da021ae3a21b76bf38058f034e8dc40d9` | 2026-07-17 | Siddharth Kothari | Remove internal review case log; public support address |
|
||||
| `f89ca0803f571a57703766a154d628def1fe3696` | 2026-07-17 | Siddharth Kothari | README: rewrite for the public launch |
|
||||
| `a8c242933a6917aaeeade383c09f0ea998d467d0` | 2026-07-17 | Siddharth Kothari | README: add pipeline diagram |
|
||||
| `b26ad3509f4a19878fb7050aad3da9eacba8d914` | 2026-07-17 | Sid / GitHub | Update README.md (demo screenshot) |
|
||||
|
||||
`upstream/main` **is** `b26ad35`. Fork `origin/main` contains those five plus three fork-only commits. **0** upstream commits are missing from the fork (ahead/behind `upstream/main...origin/main` = `0 3`).
|
||||
|
||||
### 3.3 Divergence
|
||||
|
||||
```
|
||||
6672d2b Pinscope OSS root (Faradworks)
|
||||
…
|
||||
b26ad35 upstream/main = merge-base
|
||||
├─ origin/main (+3)
|
||||
│ 4888092 Docker, remove Clerk, OSS mode
|
||||
│ 2c5d9d3 Document fork changes in README
|
||||
│ ab1c5b0 Fix Anthropic skill references and backend git support
|
||||
└─ origin/cursor/pcb-review-plan-44dd (+92 from upstream, +89 from origin/main)
|
||||
48246f3 Adapt Pinscope to DeepSeek, auto datasheets, shared library
|
||||
… DeepSeek, KiCad, layout F1, placement, impedance, rebrand 8d2b856,
|
||||
PCB job 16c606a, finding engine dc65e96, … facbaa2 (2.32.1)
|
||||
origin/cursor/deepseek-71c5 is 14 commits behind pcb-review (linear ancestor)
|
||||
```
|
||||
|
||||
`origin/main` does **not** contain DeepSeek, `periscopex`, PCB exam, or the rebrand. A clone of default `main` is still branded Pinscope plus Docker.
|
||||
|
||||
### 3.4 Periscope vs PinScope authorship on product snapshot
|
||||
|
||||
`git log --format='%an <%ae>'` on `origin/cursor/pcb-review-plan-44dd` (97 commits including upstream):
|
||||
|
||||
| Count | Author |
|
||||
| --- | --- |
|
||||
| 88 | Michele Bigi `<mikbigi@gmail.com>` |
|
||||
| 2 | manvalan `<mikbigi@gmail.com>` |
|
||||
| 1 | Michele `<mikbigi@gmail.com>` |
|
||||
| 1 | Cursor Agent `<cursoragent@cursor.com>` |
|
||||
| 3 | Siddharth Kothari `<siddkot@gmail.com>` |
|
||||
| 1 | Siddharth Kothari `<Sid@faradworks.com>` |
|
||||
| 1 | Sid `<44057927+Monacrylic@users.noreply.github.com>` |
|
||||
|
||||
PinScope authors = the five upstream commits. Periscope authors = the 92 fork commits (Michele / manvalan / Cursor Agent).
|
||||
|
||||
### 3.5 Product commit spine (fork-only, oldest → newest groups)
|
||||
|
||||
Not every SHA is repeated here; `git log origin/main..origin/cursor/pcb-review-plan-44dd` is the complete 89-commit list. Grouped:
|
||||
|
||||
1. **DeepSeek / library** (`48246f3` … `30aaf55`): OpenAI-compatible provider, local skills, datasheet fetch, catalog passives, reprocess, quote verify.
|
||||
2. **CAD / KiCad** (`e4fd6c3` … `c5b260b`): hierarchy zip, `.kicad_sch` / `.kicad_pcb` nets, cad-bridge plugin later.
|
||||
3. **Deterministic schematic checks** (`b1ee445` … `7aed64c`): decoupling, I2C, DNP, sequencing, thermal, errata, layout_rules.
|
||||
4. **Impedance / antenna** (`6fc2ac5` … `556306b`): vendored ImpedenceFinder, RF tab.
|
||||
5. **Auth** (`5a69b38`): local multi-user JWT (PinScope-named originally).
|
||||
6. **Layout F1 / Placement F2** (`2950446` … `86121e6`): domains, satellites, gated xy pack.
|
||||
7. **Rebrand** (`8d2b856`, `dc364f5`): Pinscope → Periscope, `pinscopex` → `periscopex`, `PS-*` → `PE-*`.
|
||||
8. **Deploy** (`6b0e44a` … `344ecb3`): `/root/periscope`, Docker networks `pinscope_pinscope` / `pinscope_periscope`.
|
||||
9. **PCB exam + finding engine** (`721ede4` … `facbaa2`): MODE=pcb, shared `library/extracted`, FACT/REQUIREMENT/INFERENCE.
|
||||
|
||||
---
|
||||
|
||||
## 4. File classification vs upstream
|
||||
|
||||
Rules: see §2. Full per-file table: **Appendix A**. Snapshot: 404 HEAD files vs 254 upstream files.
|
||||
|
||||
**Unmatched upstream path still absent from HEAD:** `backend/pinscopex/__init__.py` (package renamed; other `pinscopex/*` files matched as `periscopex/*`).
|
||||
|
||||
**REPLACEMENT (4)** — same path, low overlap:
|
||||
|
||||
| Path | Jaccard | Note |
|
||||
| --- | --- | --- |
|
||||
| `README.md` | 0.133 | DeepSeek/Periscope README vs Pinscope launch README |
|
||||
| `frontend/src/lib/auth.ts` | 0.093 | Local JWT + legacy `pinscope_token` vs open-core stub |
|
||||
| `frontend/src/components/layout/sidebar-auth.tsx` | 0.105 | Local account UI vs Clerk seam stub |
|
||||
| `skills/extract-pintable/SKILL.md` | 0.066 | Prompt rewritten; sibling `schema.json` / `validate.py` still DERIVED |
|
||||
|
||||
**THIRD_PARTY:** `vendor/impedancefinder/*` except `__init__.py` (empty-blob caveat). `SOURCE.md` cites https://github.com/manvalan/ImpedenceFinder commit `a0c8d0ec37c9a1b099082926e50a245778ec8d6e`. Wrapper `backend/periscopex/impedance.py` is NEW. Tests under `tests/impedancefinder/` are NEW (except empty `__init__` mis-tag).
|
||||
|
||||
**NEW (high-signal, not exhaustive):** `finding_engine.py`, PCB/placement/antenna/impedance modules, `pcb_pipeline.py` / `placement_pipeline.py`, DeepSeek/local skill/PDF ingest, local auth routers, KiCad plugin, `docs/motore-finding.md`, `docs/piano-pcb-review.md`, `scripts/update-periscope.sh`, `docker-compose.yml`.
|
||||
|
||||
**DERIVED core:** `backend/periscopex/{graph,models,parsers,parsers_edif,validate,validation_tools,resolve_passives,derating,bom_summary,taxonomy,pin_mux_check,led_current_check}.py` with Jaccard typically 0.63–0.98 vs `backend/pinscopex/*`. `parsers_edif.py`, `pin_function_tokens.py`, `taxonomy.py` are **identical blobs** after rename.
|
||||
|
||||
**UPSTREAM clusters:** `LICENSE` (identical AGPL-3.0 text), most `frontend/src` primitives / marketing assets, `frontend/public`, `skills/extract-pattern` + `extract-specs` trees, most `taxonomy/*.json`, `simple_project` netlist/BOM/PDF/design_graph, several `backend/services` and routers unchanged.
|
||||
|
||||
---
|
||||
|
||||
## 5. Architectural dependency map (PinScope → Periscope)
|
||||
|
||||
Strengths: **DIRECT** = still the same module/control flow; **INDIRECT** = new code that imports DIRECT modules; **WEAK** = naming, docs, compatibility aliases, unused seams; **NONE** = no PinScope type/control coupling observed.
|
||||
|
||||
```
|
||||
PinScope OSS (pinscopex + FastAPI + Next.js + skills)
|
||||
│ DIRECT
|
||||
├─ Design graph / BOM / netlist parsers / taxonomy / derating
|
||||
├─ LLM review loop (validate.py, validation_tools, extraction, pipeline.py)
|
||||
├─ Finding Pydantic model + normalize/dedupe
|
||||
├─ Next.js report / project / admin shell
|
||||
├─ Clerk/billing gateway seams (stubs + remaining calls)
|
||||
└─ Claude Console skills contract (manifest + upload_skills.py)
|
||||
│
|
||||
▼
|
||||
Periscope (rebrand + DeepSeek + extra jobs)
|
||||
│ INDIRECT (new jobs on the same graph/library)
|
||||
├─ MODE=pcb (pcb_pipeline → parsers_kicad_pcb, graph, validate._parse_review, finding_engine)
|
||||
├─ MODE=placement
|
||||
├─ cad-bridge / KiCad plugin
|
||||
├─ finding_engine.complete_finding() wrapping models.Finding
|
||||
└─ datasheet finder / Mouser / local skills (replaces Anthropic upload in practice)
|
||||
│
|
||||
WEAK
|
||||
├─ pinscope_version alias, pinscopex: localStorage, pinscope_token, JWT issuer pinscope-local
|
||||
├─ Docker network names pinscope_*
|
||||
├─ Faradworks terms/privacy/metadata/Twitter
|
||||
└─ CLAUDE.md still describing Console Skills while README forbids Anthropic
|
||||
│
|
||||
NONE (new engines, still may import geometry types)
|
||||
├─ antenna_geometry / antenna_rf
|
||||
└─ vendor ImpedenceFinder (third party) via vendor_path
|
||||
```
|
||||
|
||||
| Subsystem | Strength | Evidence |
|
||||
| --- | --- | --- |
|
||||
| `periscopex.graph` / `parsers*` / `models` / `taxonomy` | DIRECT | Rename + high Jaccard; `pipeline.py` still `build_graph` / `parse_bom` / `parse_netlist_any` |
|
||||
| `periscopex.validate` / `validation_tools` / `services/validation.py` | DIRECT | Same per-IC datasheet review; `complete_findings` grafted at end of validate |
|
||||
| `services/pipeline.py` + `pipeline_worker.py` | DIRECT | Same stage machine; provider swapped |
|
||||
| `finding_engine.py` | INDIRECT | NEW; imports `models.Finding`; seeds `PE-*`; used by `validate.py` and `pcb_checks.py` |
|
||||
| PCB exam (`pcb_pipeline`, `pcb_*`) | INDIRECT | NEW; `ensure_graph` uses PinScope graph; AI path reuses `_parse_review` |
|
||||
| Placement F2 | INDIRECT | NEW on `functional_groups` + graph |
|
||||
| Impedance tab | INDIRECT + THIRD_PARTY | NEW facade; solver from vendor |
|
||||
| Frontend report / wizard | DIRECT / DERIVED | Same App Router tree; FindingCard extended |
|
||||
| Local auth | REPLACEMENT + WEAK | Replaces Clerk for self-host; Clerk still in `middleware/auth.py`, admin, email |
|
||||
| Legal content | DIRECT | `terms.md` / `privacy.md` still Faradworks, Inc. |
|
||||
| `skills/extract-pattern`, `extract-specs` | DIRECT | UPSTREAM blobs |
|
||||
| `skills/extract-pintable/SKILL.md` | REPLACEMENT | Contract (schema/validate) still DERIVED |
|
||||
| `scripts/upload_skills.py` | DIRECT | UPSTREAM; README on product branch says do not run it |
|
||||
| Docker / `update-periscope.sh` | WEAK / NONE | New deploy; leftover `pinscope` host/network names |
|
||||
| `LICENSE` | DIRECT | Identical AGPL-3.0 |
|
||||
|
||||
PCB/finding-engine **code was not modified** for this audit. Observation only: PCB is already a second job, but it is not architecturally independent of PinScope’s graph, pintable cache, or review parser.
|
||||
|
||||
---
|
||||
|
||||
## 6. Package and lockfile licenses
|
||||
|
||||
Root project license: **GNU AGPL v3** text, blob-identical to upstream (`LICENSE` = UPSTREAM). README on the product branch: “AGPL-3.0, same as upstream Pinscope (Faradworks). For commercial licensing of the original, write to dev@faradworks.com.” GitHub API also reports `AGPL-3.0` on both repos. No `SPDX-License-Identifier` headers in the tree (0 files).
|
||||
|
||||
There is **no** Python lockfile (`poetry.lock` / `uv.lock` / `requirements.lock`). Versions below are **declared ranges** plus **PyPI’s current JSON license field** (fetched 2026-09-20), not the exact installed wheel in a given deploy.
|
||||
|
||||
### 6.1 `backend/requirements.txt` (direct)
|
||||
|
||||
| Requirement | PyPI `info.license` (as returned) | Classifiers / notes |
|
||||
| --- | --- | --- |
|
||||
| fastapi | MIT | |
|
||||
| uvicorn | BSD-3-Clause | |
|
||||
| openai | Apache-2.0 | OSI Apache; **added on fork** |
|
||||
| anthropic | MIT | still declared; product README says do not use Anthropic |
|
||||
| google-genai | Apache-2.0 | |
|
||||
| pydantic | MIT | |
|
||||
| pydantic-settings | MIT | OSI MIT |
|
||||
| python-multipart | Apache-2.0 | |
|
||||
| sse-starlette | BSD-3-Clause | |
|
||||
| python-dotenv | BSD-3-Clause | |
|
||||
| openpyxl | MIT | |
|
||||
| google-cloud-storage | Apache-2.0 | |
|
||||
| google-cloud-run | Apache-2.0 | |
|
||||
| google-api-python-client | `Apache 2.0` (PyPI spelling, not SPDX token) | OSI Apache |
|
||||
| pypdf | BSD-3-Clause | |
|
||||
| pymupdf | `Dual Licensed - GNU AFFERO GPL 3.0 or Artifex Commercial License` | **added on fork**; dual license from PyPI, not inferred |
|
||||
| PyJWT | MIT | extras `[crypto]` |
|
||||
| cryptography | Apache-2.0 OR BSD-3-Clause | |
|
||||
| httpx | BSD-3-Clause | **added on fork** |
|
||||
| packaging | Apache-2.0 OR BSD-2-Clause | |
|
||||
| shapely | `BSD 3-Clause` (PyPI spelling) | OSI BSD; **added on fork** (ImpedenceFinder) |
|
||||
| PyYAML | MIT | **added on fork** |
|
||||
|
||||
Transitive Python licenses were **not** enumerated (no lockfile). Marked INVESTIGATE.
|
||||
|
||||
### 6.2 Frontend direct dependencies (`package-lock.json` `packages["node_modules/<name>"]`)
|
||||
|
||||
Lockfile version field is `2.10.0` while `package.json` is `2.26.3` (quality note §8). Every lock `packages` entry has a `license` field (827/827).
|
||||
|
||||
| Package | Locked version | `license` field |
|
||||
| --- | --- | --- |
|
||||
| @base-ui/react | 1.3.0 | MIT |
|
||||
| @xyflow/react | 12.10.2 | MIT |
|
||||
| @types/dagre | 0.7.54 | MIT |
|
||||
| class-variance-authority | 0.7.1 | Apache-2.0 |
|
||||
| clsx | 2.1.1 | MIT |
|
||||
| dagre | 0.8.5 | MIT |
|
||||
| lucide-react | 1.8.0 | ISC |
|
||||
| next | 16.2.3 | MIT |
|
||||
| next-themes | 0.4.6 | MIT |
|
||||
| react / react-dom | 19.2.4 | MIT |
|
||||
| react-markdown | 10.1.0 | MIT |
|
||||
| react-pdf | 10.4.1 | MIT |
|
||||
| shadcn | 4.2.0 | MIT |
|
||||
| tailwind-merge | 3.5.0 | MIT |
|
||||
| tw-animate-css | 1.4.0 | MIT |
|
||||
| xlsx | 0.18.5 | Apache-2.0 |
|
||||
| @tailwindcss/postcss / tailwindcss | 4.2.2 | MIT |
|
||||
| typescript | 5.9.3 | Apache-2.0 |
|
||||
| eslint / eslint-config-next | 9.39.4 / 16.2.3 | MIT |
|
||||
| @types/node | 20.19.39 | MIT |
|
||||
| @types/react / react-dom | 19.2.14 / 19.2.3 | MIT |
|
||||
|
||||
### 6.3 Transitive npm license histogram (lockfile fields only)
|
||||
|
||||
MIT 684; ISC 44; Apache-2.0 43; MPL-2.0 13 (e.g. `axe-core`, `lightningcss*`); BSD-2-Clause 11; LGPL-3.0-or-later 10 (`@img/sharp-libvips-*`); BSD-3-Clause 9; BlueOak-1.0.0 3; Apache-2.0 AND LGPL-3.0-or-later 3; 0BSD 2; plus one each of Apache-2.0 AND LGPL-3.0-or-later AND MIT, Python-2.0 (`argparse`), CC-BY-4.0 (`caniuse-lite`), CC0-1.0, (MIT OR CC0-1.0).
|
||||
|
||||
**Copyleft transitive (frontend):** LGPL-3.0-or-later via sharp/libvips optional platform packages; MPL-2.0 via lightningcss/axe-core. Not removed; recorded.
|
||||
|
||||
### 6.4 Vendored ImpedenceFinder
|
||||
|
||||
| Probe | Result |
|
||||
| --- | --- |
|
||||
| GitHub `license` on `manvalan/ImpedenceFinder` | JSON `license: null` |
|
||||
| `LICENSE` at vendored commit `a0c8d0ec…` | HTTP 404 |
|
||||
| `pyproject.toml` on default `main` | **no** `[project].license` / license-files |
|
||||
| README | **no** license section (Italian functional README) |
|
||||
| Vendored Python headers | describe KiCad formula provenance; **no** SPDX, **no** copyright line |
|
||||
|
||||
License of the vendored snapshot is **UNKNOWN from evidence**. Do not treat as MIT/AGPL by name. INVESTIGATE before any additional distribution story.
|
||||
|
||||
---
|
||||
|
||||
## 7. String search: Pinscope / PinScope / pinscope / Faradworks / SPDX / copyright
|
||||
|
||||
**Do not remove these in Phase 0.** Classification only.
|
||||
|
||||
| Needle | Files | Match lines (approx) | Class of hits |
|
||||
| --- | --- | --- | --- |
|
||||
| `PinScope` | 0 | 0 | none |
|
||||
| `SPDX` | 0 | 0 | none in source |
|
||||
| `copyright` / `Copyright` | 1 (`LICENSE`) | AGPL template + FSF | UPSTREAM license text |
|
||||
| `Pinscope` | 3 | 4 | README license line; changelog rebrand notes; deploy CORS comment |
|
||||
| `pinscope` (any case via listing) | 9 | 30 | legacy IDs, paths, Docker networks, clone URL |
|
||||
| `Faradworks` | 8 | 67 | terms, privacy, marketing, layout metadata, footers |
|
||||
| `faradworks` | 7 | 21 | `dev@faradworks.com`, faradworks.com, `@getFaradWorks` |
|
||||
|
||||
### 7.1 Compatibility / ops (KEEP or ISOLATE — still serving a migration)
|
||||
|
||||
| Location | Hit | Role |
|
||||
| --- | --- | --- |
|
||||
| `backend/services/projects.py` | `pinscope_version` alias | Reads old `project.json` |
|
||||
| `frontend/src/lib/api.ts` | `p.pinscope_version` | Same |
|
||||
| `frontend/src/lib/auth.ts` | `LEGACY_TOKEN_STORAGE_KEY = "pinscope_token"` | Pre-rebrand JWT |
|
||||
| `frontend/src/lib/storage-keys.ts` | still **writes** `pinscopex:*` keys | Comment says migrate; implementation still PinScope prefix |
|
||||
| `scripts/update-periscope.sh` / `docker-compose.yml` | `pinscope_pinscope`, `pinscope.michelebigi.it`, clone `manvalan/pinscope` | Live VPS topology |
|
||||
| `frontend/content/changelog.md` | historical Pinscope / `PS-*` | Documentation of migration |
|
||||
|
||||
### 7.2 Faradworks identity still on the Periscope product UI (REPLACE for independence; not “delete PinScope code”)
|
||||
|
||||
| Location | Hit |
|
||||
| --- | --- |
|
||||
| `frontend/src/app/layout.tsx` | `authors` / `creator` / `publisher`: Faradworks; `url: https://faradworks.com` |
|
||||
| `frontend/src/lib/site.ts` | `TWITTER_HANDLE = "@getFaradWorks"`; `SITE_NAME` already `"Periscope"` |
|
||||
| `frontend/src/components/legal/*` | `© {year} Faradworks` |
|
||||
| `frontend/content/terms.md` | Entire TOS: Faradworks, Inc. Delaware; ownership of “the Service”; `dev@faradworks.com` |
|
||||
| `frontend/content/privacy.md` | Faradworks contact |
|
||||
| `frontend/src/app/(marketing)/page.tsx`, `contact/*` | `dev@faradworks.com` |
|
||||
| `README.md` | commercial license → `dev@faradworks.com` |
|
||||
|
||||
`terms.md` still defines the operator as Faradworks while `SITE_URL` defaults to `https://periscope.michelebigi.it`. That is a **product-identity coupling**, not a parser coupling.
|
||||
|
||||
### 7.3 © in LICENSE
|
||||
|
||||
FSF copyright on the AGPL **license document** itself (2007). That is expected and KEEP. No per-file `Copyright (C) Faradworks` headers were found in `.py`/`.ts` (search limited as in §2). Absence of headers ≠ absence of copyright; the fork inherits AGPL obligations from the parent work.
|
||||
|
||||
---
|
||||
|
||||
## 8. Duplicated concepts and partial migrations
|
||||
|
||||
| Concept | PinScope side (still present) | Periscope side | Migration state |
|
||||
| --- | --- | --- | --- |
|
||||
| Package name | `backend/pinscopex` gone; empty `__init__` only unmatched | `backend/periscopex` | **Done** (rename); imports updated |
|
||||
| Finding IDs | changelog `PS-*` | `PE-*` in finding_engine | **Done** for new IDs; old reports not in repo |
|
||||
| App version field | `pinscope_version` alias | `periscope_version` | **Partial** — dual read |
|
||||
| Browser storage | `pinscopex:` keys still **produced** | comment claims migration | **Partial / inverted** |
|
||||
| Auth | Clerk JWKS, Clerk admin/email, `ClerkThemeProvider` | local JWT `AUTH_JWT_SECRET` | **Partial** — two stacks |
|
||||
| LLM | `anthropic` dep, `upload_skills.py`, CLAUDE.md Console Skills | DeepSeek + `local_skill.py` + README “do not use Anthropic” | **Partial** |
|
||||
| Review findings | `validate._parse_review`, normalize/dedupe | `finding_engine.complete_finding` | **Layered**, not replaced |
|
||||
| Jobs | `MODE=run` analysis | `MODE=pcb`, `MODE=placement` | **Additive** three mutexed jobs |
|
||||
| Default branch vs product | GitHub `main` = Docker Pinscope OSS | pcb-review = 2.32.1 | **Not merged** |
|
||||
| Docker networks | `pinscope_pinscope` (Caddy) | `pinscope_periscope` / aliases | **Partial** rename |
|
||||
| Legal | Faradworks TOS/privacy | SITE_NAME Periscope | **Partial** |
|
||||
| Changelog vs lockfile | `package-lock.json` `"version": "2.10.0"` | `package.json` `2.26.3`, changelog 2.32.1 | **Drift** |
|
||||
| Impedance | (none upstream) | vendor + `periscopex.impedance` | NEW + THIRD_PARTY |
|
||||
| Open-core seams | `billing_hook`, proxy.ts, clerk-theme | OSS no-op + local auth | Stubs KEEP for API shape; identity REPLACE |
|
||||
|
||||
---
|
||||
|
||||
## 9. Technical quality observations (no rewrite proposals)
|
||||
|
||||
These are facts about the snapshot, not a license to rip out PinScope.
|
||||
|
||||
1. **Default branch is not the product.** Anyone cloning `manvalan/periscope` without a branch flag gets pre-DeepSeek Pinscope + Docker (`ab1c5b0`). Live notes refer to 2.32.1 on the PCB branch.
|
||||
2. **Docs disagree:** `CLAUDE.md` (DERIVED, Jaccard 0.67) still titles Periscope but describes Claude Console Skills as required. Product `README.md` (REPLACEMENT) says skills are local and Anthropic must not be used.
|
||||
3. **Finding engine is a clamp, not a new IR.** `validate.py` still produces `Finding` objects; `complete_findings` fills FACT/REQUIREMENT/INFERENCE. Replacing PinScope review in one shot would discard the working schematic loop.
|
||||
4. **PCB job correctly refuses auto-place** (comments in `pcb_pipeline.py`) but **depends** on schematic graph + pintable cache (“run schematic review first”). That is coupling, not a bug by itself.
|
||||
5. **Empty `__init__.py` files** share one git blob; mechanical rename detection over-claims derivation (§2).
|
||||
6. **`frontend/package-lock.json` is almost identical to upstream** (Jaccard 0.999) while app version moved to 2.26.3/2.32.1 — lock **name-version** stale; dependency licenses still readable from the lock.
|
||||
7. **PyMuPDF dual AGPL/commercial** on PyPI is a distribution constraint for binaries/SaaS (AGPL network clause already applies via root LICENSE). Recorded, not changed.
|
||||
8. **ImpedenceFinder** has no license metadata. KiCad formula comments are provenance of **math**, not a license grant.
|
||||
9. **xlsx@0.18.5** lock field is Apache-2.0 (not guessed from “SheetJS”).
|
||||
10. **Auth production guard** still mentions Clerk (`backend/main.py`, `update-periscope.sh` dies if `ENVIRONMENT=production` without Clerk). Local JWT exists but deploy script language is mixed.
|
||||
11. **No per-source-file copyright/SPDX** — harder provenance later; AGPL still applies to the combined work.
|
||||
12. Tests for NEW modules are substantial (`tests/test_pcb_review.py`, `test_finding_engine.py`, etc.) — quality of *additions* is high relative to unmerged `main`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Disposition (KEEP / ISOLATE / REPLACE / REMOVE / INVESTIGATE)
|
||||
|
||||
Independence means Periscope can evolve its product, legal identity, and review contract without pretending to be Faradworks/Pinscope. It does **not** mean deleting `graph.py`.
|
||||
|
||||
| Area | Action | Why |
|
||||
| --- | --- | --- |
|
||||
| AGPL `LICENSE` blob | **KEEP** | Identical upstream license; changing it is out of scope and would not create independence |
|
||||
| Parsers, graph, taxonomy, BOM, derating, EDIF | **KEEP** | DIRECT lineage; this *is* the schematic core; replacing is a later engineering program |
|
||||
| `models.Finding` + normalize/dedupe | **KEEP** + **ISOLATE** | Keep types; route new semantics through `finding_engine` |
|
||||
| `validate.py` LLM loop | **KEEP** for now; **ISOLATE** provider/skills | Working product; independence = provider + finding contract, not a greenfield reviewer |
|
||||
| PCB / placement / antenna / finding_engine / KiCad plugin | **KEEP** | NEW Periscope surface |
|
||||
| `vendor/impedancefinder` | **ISOLATE** + **INVESTIGATE** | Boundary already in `impedance.py`; license unknown |
|
||||
| Faradworks TOS, privacy, footers, layout authors, Twitter, contact email | **REPLACE** | Operator identity; largest *non-engine* coupling |
|
||||
| `pinscope_version` / JWT issuer / CORS host aliases | **ISOLATE** then expire | Needed for live data; not the architecture |
|
||||
| `storage-keys.ts` still writing `pinscopex:` | **REPLACE** (write path) after dual-read | Partial migration bug-shaped |
|
||||
| Clerk seams (`clerk-theme`, JWKS, admin Clerk API, email) | **ISOLATE**; **REMOVE** only when local auth is the only supported mode | Dual stack |
|
||||
| `anthropic` dependency, `upload_skills.py`, Console-oriented CLAUDE.md | **ISOLATE**; **REMOVE** later if DeepSeek-only is policy | README already forbids Anthropic |
|
||||
| GitHub `main` vs pcb-review | **INVESTIGATE** (process) | Merge/publish strategy; do not detach fork |
|
||||
| PyMuPDF dual license + Python transitives | **INVESTIGATE** | No pip lock |
|
||||
| ImpedenceFinder license from author | **INVESTIGATE** | Blocker for *clean* third-party story, not for reading code |
|
||||
| `simple_project` / how-it-works.svg | **KEEP** | UPSTREAM fixtures; rebrand docs separately |
|
||||
| Billing hook / gateway stubs | **KEEP** signatures; **ISOLATE** comments that imply Faradworks cloud | Open-core seam |
|
||||
| PinScope *strings* in changelog | **KEEP** | Historical accuracy |
|
||||
|
||||
**REMOVE** is empty for Phase 0. Nothing should be deleted on the basis of this audit alone.
|
||||
|
||||
---
|
||||
|
||||
## 11. Phase 1 sequence (recommended order; not this change)
|
||||
|
||||
Phase 1 should increase **independence**, not maximize diff against PinScope.
|
||||
|
||||
1. **Land this audit** on a branch that does not rewrite engines (`docs/development/PINSCOPE_AUDIT.md` only).
|
||||
2. **Branch topology:** decide how GitHub `main` relates to `cursor/pcb-review-plan-44dd` without detaching the fork (merge/PR of product onto `main`, or document that `main` is archival). Until then, every clone is the wrong tree.
|
||||
3. **Operator identity:** replace Faradworks TOS/privacy/contact/metadata/Twitter/footers with Periscope operator documents; keep AGPL and a factual “derived from Faradworks/Pinscope” attribution (README already has a form of this).
|
||||
4. **Compatibility fence:** one module/doc for legacy `pinscope_*` keys, JWT issuer, CORS, Docker network names; stop writing new `pinscopex:` storage keys.
|
||||
5. **Auth ISOLATE:** make the self-host path (local JWT) the documented default; keep Clerk behind a named seam so admin/email do not assume Clerk.
|
||||
6. **LLM ISOLATE:** document DeepSeek as the supported provider; treat Anthropic SDK + `upload_skills.py` as optional/legacy so CLAUDE.md matches README.
|
||||
7. **Finding contract:** keep using `finding_engine` as the independence layer on top of `Finding`; do not rewrite `validate.py` in Phase 1.
|
||||
8. **Vendor license:** obtain an explicit license for ImpedenceFinder (or stop distributing the vendor tree until known).
|
||||
9. **Python lock:** add a lockfile so transitive licenses can be audited like npm.
|
||||
10. **Only then** consider isolating parsers/graph behind a narrower Periscope API — a large KEEP surface, not a Phase 1 delete.
|
||||
|
||||
---
|
||||
|
||||
## 12. Unresolved questions and evidence gaps
|
||||
|
||||
1. **Intent of GitHub `main`:** accidental staleness vs deliberate “OSS mirror of upstream + Docker”? No PRs exist to explain it.
|
||||
2. **Legal operator of periscope.michelebigi.it** vs documents that still say Faradworks, Inc. — this audit cannot assign a legal entity.
|
||||
3. **ImpedenceFinder license** (author, KiCad cpp port implications). GitHub API `license` is null.
|
||||
4. **PyMuPDF** AGPL vs Artifex commercial: which grant is used in the VPS deploy?
|
||||
5. **Python transitive licenses** without a lockfile.
|
||||
6. **Whether Anthropic is still reachable** if env keys exist despite README (code path exists; runtime not executed here).
|
||||
7. **Private Faradworks gateway** files mentioned in upstream CLAUDE.md — not in this repo; cannot audit what the parent still replaces in cloud builds.
|
||||
8. **Trademark** “Pinscope” / “Periscope” / “Faradworks”: search found **use**, not a registration database. No legal conclusion.
|
||||
9. **Empty-file DERIVED tags** — see §2; raw vs adjusted counts.
|
||||
10. **`origin` on the analyst clone** included a local `upstream` remote for fetch-only comparison. GitHub fork remotes were not edited; confirm live GitHub still has `parent: Faradworks/Pinscope` after this docs-only PR.
|
||||
11. **mbparks/pinscope** (Arduino) is a **different project**; zero code overlap observed; ignored.
|
||||
12. **Exact installed npm/pip versions on production** (`/root/periscope`) were not inspected; licenses are from lock + PyPI JSON as of 2026-09-20.
|
||||
|
||||
---
|
||||
|
||||
## Appendix A — Per-file classification
|
||||
|
||||
Evidence strings are mechanical (SHA / Jaccard / path). Empty-blob rename caveat applies to three `__init__.py` files listed as DERIVED.
|
||||
### UPSTREAM (125)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
| `.github/workflows/ci.yml` | identical blob SHA |
|
||||
| `LICENSE` | identical blob SHA |
|
||||
| `backend/__init__.py` | identical blob SHA |
|
||||
| `backend/middleware/__init__.py` | identical blob SHA |
|
||||
| `backend/routers/__init__.py` | identical blob SHA |
|
||||
| `backend/routers/deps.py` | identical blob SHA |
|
||||
| `backend/routers/feedback.py` | identical blob SHA |
|
||||
| `backend/routers/survey.py` | identical blob SHA |
|
||||
| `backend/services/__init__.py` | identical blob SHA |
|
||||
| `backend/services/admin_settings.py` | identical blob SHA |
|
||||
| `backend/services/billing_hook.py` | identical blob SHA |
|
||||
| `backend/services/purple_parts.py` | identical blob SHA |
|
||||
| `backend/services/storage.py` | identical blob SHA |
|
||||
| `backend/services/storage_gcs.py` | identical blob SHA |
|
||||
| `backend/services/survey.py` | identical blob SHA |
|
||||
| `docs/how-it-works.svg` | identical blob SHA |
|
||||
| `frontend/.gitignore` | identical blob SHA |
|
||||
| `frontend/AGENTS.md` | identical blob SHA |
|
||||
| `frontend/README.md` | identical blob SHA |
|
||||
| `frontend/components.json` | identical blob SHA |
|
||||
| `frontend/eslint.config.mjs` | identical blob SHA |
|
||||
| `frontend/postcss.config.mjs` | identical blob SHA |
|
||||
| `frontend/public/datasheet.gif` | identical blob SHA |
|
||||
| `frontend/public/derating.png` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/altium.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/autodesk.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/cadence.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/easyeda.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/kicad.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/orcad.svg` | identical blob SHA |
|
||||
| `frontend/public/eda-logos/siemens.svg` | identical blob SHA |
|
||||
| `frontend/public/examples/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf` | identical blob SHA |
|
||||
| `frontend/public/examples/TI-MSP-KICAD9-TUTORIAL.asc` | identical blob SHA |
|
||||
| `frontend/public/examples/TI-MSP-KICAD9-TUTORIAL.csv` | identical blob SHA |
|
||||
| `frontend/public/faradworks-logo-white.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/android-chrome-192x192.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/android-chrome-512x512.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/apple-touch-icon.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/favicon-16x16.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/favicon-32x32.png` | identical blob SHA |
|
||||
| `frontend/public/favicon_io/favicon.ico` | identical blob SHA |
|
||||
| `frontend/public/file.svg` | identical blob SHA |
|
||||
| `frontend/public/globe.svg` | identical blob SHA |
|
||||
| `frontend/public/next.svg` | identical blob SHA |
|
||||
| `frontend/public/power-tree.gif` | identical blob SHA |
|
||||
| `frontend/public/report.png` | identical blob SHA |
|
||||
| `frontend/public/vercel.svg` | identical blob SHA |
|
||||
| `frontend/public/window.svg` | identical blob SHA |
|
||||
| `frontend/scripts/sync-version.mjs` | identical blob SHA |
|
||||
| `frontend/src/app/(app)/api/graph/[id]/route.ts` | identical blob SHA |
|
||||
| `frontend/src/app/(app)/api/projects/route.ts` | identical blob SHA |
|
||||
| `frontend/src/app/(app)/api/report/[id]/route.ts` | identical blob SHA |
|
||||
| `frontend/src/app/(marketing)/contact/contact-form.tsx` | identical blob SHA |
|
||||
| `frontend/src/app/(marketing)/layout.tsx` | identical blob SHA |
|
||||
| `frontend/src/app/favicon.ico` | identical blob SHA |
|
||||
| `frontend/src/app/globals.css` | identical blob SHA |
|
||||
| `frontend/src/app/layout.tsx` | identical blob SHA |
|
||||
| `frontend/src/app/robots.ts` | identical blob SHA |
|
||||
| `frontend/src/app/sitemap.ts` | identical blob SHA |
|
||||
| `frontend/src/app/twitter-image.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/analytics/reddit-pixel-match-keys.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/analytics/reddit-pixel.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/billing/credits-context.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/billing/paused-run-banner.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/feedback/feedback-dialog.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/legal/file-guide-page.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/marketing/pricing-section.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/pdf/pdf-viewer-sheet.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/progress/pipeline-stepper.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/report/finding-comments.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/report/mention-input.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/report/reviewed-section.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/report/status-badge.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/theme/clerk-theme-provider.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/theme/theme-provider.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/theme/theme-toggle.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/alert-dialog.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/badge.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/button.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/card.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/collapsible.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/dialog.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/dropdown-menu.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/input.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/label.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/progress.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/scroll-area.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/select.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/separator.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/sheet.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/skeleton.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/tabs.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/textarea.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/toast.tsx` | identical blob SHA |
|
||||
| `frontend/src/components/ui/tooltip.tsx` | identical blob SHA |
|
||||
| `frontend/src/hooks/use-auth-api.ts` | identical blob SHA |
|
||||
| `frontend/src/lib/mock-data.ts` | identical blob SHA |
|
||||
| `frontend/src/lib/utils.ts` | identical blob SHA |
|
||||
| `frontend/src/proxy.ts` | identical blob SHA |
|
||||
| `frontend/tsconfig.json` | identical blob SHA |
|
||||
| `scripts/backfill_passive_subtype.py` | identical blob SHA |
|
||||
| `scripts/clear_rules_from_extractions.py` | identical blob SHA |
|
||||
| `scripts/gc_orphan_blobs.py` | identical blob SHA |
|
||||
| `scripts/migrate_datasheets_to_blobs.py` | identical blob SHA |
|
||||
| `scripts/migrate_datasheets_to_library.py` | identical blob SHA |
|
||||
| `scripts/upload_skills.py` | identical blob SHA |
|
||||
| `simple_project/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf` | identical blob SHA |
|
||||
| `simple_project/TI-MSP-KICAD9-TUTORIAL.asc` | identical blob SHA |
|
||||
| `simple_project/TI-MSP-KICAD9-TUTORIAL.csv` | identical blob SHA |
|
||||
| `simple_project/design_graph.json` | identical blob SHA |
|
||||
| `skills/extract-pattern/SKILL.md` | identical blob SHA |
|
||||
| `skills/extract-pattern/schema.json` | identical blob SHA |
|
||||
| `skills/extract-pattern/validate.py` | identical blob SHA |
|
||||
| `skills/extract-specs/SKILL.md` | identical blob SHA |
|
||||
| `skills/extract-specs/schema.json` | identical blob SHA |
|
||||
| `skills/extract-specs/validate.py` | identical blob SHA |
|
||||
| `taxonomy/connector.json` | identical blob SHA |
|
||||
| `taxonomy/discrete.json` | identical blob SHA |
|
||||
| `taxonomy/fuse.json` | identical blob SHA |
|
||||
| `taxonomy/ic.json` | identical blob SHA |
|
||||
| `taxonomy/switch.json` | identical blob SHA |
|
||||
| `taxonomy/test_point.json` | identical blob SHA |
|
||||
| `taxonomy/transformer.json` | identical blob SHA |
|
||||
| `tests/__init__.py` | identical blob SHA |
|
||||
| `tests/test_cache_breakpoint_limit.py` | identical blob SHA |
|
||||
|
||||
### DERIVED (127)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
| `.dockerignore` | same path; line-set Jaccard=0.857; shared_lines=12; head_lines=13; upstream_lines=13 |
|
||||
| `.gitignore` | same path; line-set Jaccard=0.963; shared_lines=52; head_lines=54; upstream_lines=52 |
|
||||
| `CLAUDE.md` | same path; line-set Jaccard=0.669; shared_lines=89; head_lines=111; upstream_lines=111 |
|
||||
| `backend/.env.example` | same path; line-set Jaccard=0.236; shared_lines=26; head_lines=77; upstream_lines=59 |
|
||||
| `backend/CLAUDE.md` | same path; line-set Jaccard=0.847; shared_lines=111; head_lines=121; upstream_lines=121 |
|
||||
| `backend/Dockerfile` | same path; line-set Jaccard=0.600; shared_lines=18; head_lines=25; upstream_lines=23 |
|
||||
| `backend/_version.py` | same path; line-set Jaccard=0.812; shared_lines=26; head_lines=29; upstream_lines=29 |
|
||||
| `backend/config.py` | same path; line-set Jaccard=0.628; shared_lines=172; head_lines=261; upstream_lines=185 |
|
||||
| `backend/main.py` | same path; line-set Jaccard=0.715; shared_lines=108; head_lines=138; upstream_lines=121 |
|
||||
| `backend/middleware/auth.py` | same path; line-set Jaccard=0.372; shared_lines=42; head_lines=84; upstream_lines=71 |
|
||||
| `backend/periscopex/__init__.py` | identical blob to upstream backend/__init__.py (rename/move) renamed_from=`backend/__init__.py` |
|
||||
| `backend/periscopex/bom_summary.py` | path absent; matched upstream backend/pinscopex/bom_summary.py Jaccard=0.9231; shared_lines=72 renamed_from=`backend/pinscopex/bom_summary.py` |
|
||||
| `backend/periscopex/derating.py` | path absent; matched upstream backend/pinscopex/derating.py Jaccard=0.6358; shared_lines=96 renamed_from=`backend/pinscopex/derating.py` |
|
||||
| `backend/periscopex/graph.py` | path absent; matched upstream backend/pinscopex/graph.py Jaccard=0.7812; shared_lines=275 renamed_from=`backend/pinscopex/graph.py` |
|
||||
| `backend/periscopex/led_current_check.py` | path absent; matched upstream backend/pinscopex/led_current_check.py Jaccard=0.983; shared_lines=231 renamed_from=`backend/pinscopex/led_current_check.py` |
|
||||
| `backend/periscopex/models.py` | path absent; matched upstream backend/pinscopex/models.py Jaccard=0.7241; shared_lines=252 renamed_from=`backend/pinscopex/models.py` |
|
||||
| `backend/periscopex/parsers.py` | path absent; matched upstream backend/pinscopex/parsers.py Jaccard=0.6929; shared_lines=185 renamed_from=`backend/pinscopex/parsers.py` |
|
||||
| `backend/periscopex/parsers_edif.py` | identical blob to upstream backend/pinscopex/parsers_edif.py (rename/move) renamed_from=`backend/pinscopex/parsers_edif.py` |
|
||||
| `backend/periscopex/pin_function_tokens.py` | identical blob to upstream backend/pinscopex/pin_function_tokens.py (rename/move) renamed_from=`backend/pinscopex/pin_function_tokens.py` |
|
||||
| `backend/periscopex/pin_mux_check.py` | path absent; matched upstream backend/pinscopex/pin_mux_check.py Jaccard=0.9379; shared_lines=136 renamed_from=`backend/pinscopex/pin_mux_check.py` |
|
||||
| `backend/periscopex/resolve_passives.py` | path absent; matched upstream backend/pinscopex/resolve_passives.py Jaccard=0.9652; shared_lines=388 renamed_from=`backend/pinscopex/resolve_passives.py` |
|
||||
| `backend/periscopex/taxonomy.py` | identical blob to upstream backend/pinscopex/taxonomy.py (rename/move) renamed_from=`backend/pinscopex/taxonomy.py` |
|
||||
| `backend/periscopex/utils.py` | path absent; matched upstream backend/pinscopex/utils.py Jaccard=0.8824; shared_lines=15 renamed_from=`backend/pinscopex/utils.py` |
|
||||
| `backend/periscopex/validate.py` | path absent; matched upstream backend/pinscopex/validate.py Jaccard=0.8792; shared_lines=793 renamed_from=`backend/pinscopex/validate.py` |
|
||||
| `backend/periscopex/validation_tools.py` | path absent; matched upstream backend/pinscopex/validation_tools.py Jaccard=0.7846; shared_lines=499 renamed_from=`backend/pinscopex/validation_tools.py` |
|
||||
| `backend/pipeline_worker.py` | same path; line-set Jaccard=0.817; shared_lines=94; head_lines=109; upstream_lines=100 |
|
||||
| `backend/requirements.txt` | same path; line-set Jaccard=0.696; shared_lines=16; head_lines=22; upstream_lines=17 |
|
||||
| `backend/routers/admin.py` | same path; line-set Jaccard=0.831; shared_lines=404; head_lines=429; upstream_lines=461 |
|
||||
| `backend/routers/contact.py` | same path; line-set Jaccard=0.925; shared_lines=98; head_lines=102; upstream_lines=102 |
|
||||
| `backend/routers/pipeline.py` | same path; line-set Jaccard=0.467; shared_lines=236; head_lines=490; upstream_lines=251 |
|
||||
| `backend/routers/projects.py` | same path; line-set Jaccard=0.696; shared_lines=626; head_lines=810; upstream_lines=716 |
|
||||
| `backend/routers/reports.py` | same path; line-set Jaccard=0.551; shared_lines=150; head_lines=268; upstream_lines=154 |
|
||||
| `backend/services/api_logs.py` | same path; line-set Jaccard=0.775; shared_lines=79; head_lines=100; upstream_lines=81 |
|
||||
| `backend/services/cost_estimator.py` | same path; line-set Jaccard=0.961; shared_lines=292; head_lines=298; upstream_lines=298 |
|
||||
| `backend/services/datasheet_store.py` | same path; line-set Jaccard=0.586; shared_lines=102; head_lines=168; upstream_lines=108 |
|
||||
| `backend/services/dedupe_findings.py` | same path; line-set Jaccard=0.923; shared_lines=300; head_lines=309; upstream_lines=316 |
|
||||
| `backend/services/digikey.py` | same path; line-set Jaccard=0.679; shared_lines=188; head_lines=256; upstream_lines=209 |
|
||||
| `backend/services/email.py` | same path; line-set Jaccard=0.933; shared_lines=610; head_lines=632; upstream_lines=632 |
|
||||
| `backend/services/event_bridge.py` | same path; line-set Jaccard=0.881; shared_lines=133; head_lines=145; upstream_lines=139 |
|
||||
| `backend/services/extraction.py` | same path; line-set Jaccard=0.772; shared_lines=563; head_lines=711; upstream_lines=581 |
|
||||
| `backend/services/job_runner.py` | same path; line-set Jaccard=0.744; shared_lines=203; head_lines=260; upstream_lines=216 |
|
||||
| `backend/services/llm/__init__.py` | same path; line-set Jaccard=0.838; shared_lines=31; head_lines=34; upstream_lines=34 |
|
||||
| `backend/services/llm/anthropic_provider.py` | same path; line-set Jaccard=0.911; shared_lines=276; head_lines=297; upstream_lines=282 |
|
||||
| `backend/services/llm/base.py` | same path; line-set Jaccard=0.897; shared_lines=70; head_lines=73; upstream_lines=75 |
|
||||
| `backend/services/llm/factory.py` | same path; line-set Jaccard=0.806; shared_lines=54; head_lines=62; upstream_lines=59 |
|
||||
| `backend/services/llm/gemini_provider.py` | same path; line-set Jaccard=0.958; shared_lines=276; head_lines=283; upstream_lines=281 |
|
||||
| `backend/services/llm/pricing.py` | same path; line-set Jaccard=0.742; shared_lines=66; head_lines=85; upstream_lines=70 |
|
||||
| `backend/services/llm/types.py` | same path; line-set Jaccard=0.883; shared_lines=68; head_lines=75; upstream_lines=70 |
|
||||
| `backend/services/normalize_findings.py` | same path; line-set Jaccard=0.945; shared_lines=430; head_lines=440; upstream_lines=445 |
|
||||
| `backend/services/pipeline.py` | same path; line-set Jaccard=0.657; shared_lines=1157; head_lines=1569; upstream_lines=1350 |
|
||||
| `backend/services/projects.py` | same path; line-set Jaccard=0.630; shared_lines=511; head_lines=789; upstream_lines=533 |
|
||||
| `backend/services/validation.py` | same path; line-set Jaccard=0.816; shared_lines=738; head_lines=863; upstream_lines=779 |
|
||||
| `backend/skills_manifest.json` | same path; line-set Jaccard=0.417; shared_lines=10; head_lines=17; upstream_lines=17 |
|
||||
| `frontend/CLAUDE.md` | same path; line-set Jaccard=0.882; shared_lines=75; head_lines=80; upstream_lines=80 |
|
||||
| `frontend/content/changelog.md` | same path; line-set Jaccard=0.198; shared_lines=58; head_lines=285; upstream_lines=66 |
|
||||
| `frontend/content/file-guide.md` | same path; line-set Jaccard=0.702; shared_lines=87; head_lines=107; upstream_lines=104 |
|
||||
| `frontend/content/privacy.md` | same path; line-set Jaccard=0.943; shared_lines=100; head_lines=103; upstream_lines=103 |
|
||||
| `frontend/content/terms.md` | same path; line-set Jaccard=0.936; shared_lines=176; head_lines=182; upstream_lines=182 |
|
||||
| `frontend/next.config.ts` | same path; line-set Jaccard=0.710; shared_lines=44; head_lines=59; upstream_lines=47 |
|
||||
| `frontend/package-lock.json` | same path; line-set Jaccard=0.999; shared_lines=4170; head_lines=4172; upstream_lines=4172 |
|
||||
| `frontend/package.json` | same path; line-set Jaccard=0.953; shared_lines=41; head_lines=42; upstream_lines=42 |
|
||||
| `frontend/public/favicon_io/site.webmanifest` | same path; line-set Jaccard=0.789; shared_lines=15; head_lines=17; upstream_lines=17 |
|
||||
| `frontend/src/app/(app)/admin/page.tsx` | same path; line-set Jaccard=0.983; shared_lines=1188; head_lines=1205; upstream_lines=1191 |
|
||||
| `frontend/src/app/(app)/dashboard/page.tsx` | same path; line-set Jaccard=0.980; shared_lines=300; head_lines=303; upstream_lines=303 |
|
||||
| `frontend/src/app/(app)/feedback/page.tsx` | same path; line-set Jaccard=0.981; shared_lines=102; head_lines=103; upstream_lines=103 |
|
||||
| `frontend/src/app/(app)/layout.tsx` | same path; line-set Jaccard=0.545; shared_lines=18; head_lines=27; upstream_lines=24 |
|
||||
| `frontend/src/app/(app)/project/[id]/page.tsx` | same path; line-set Jaccard=0.757; shared_lines=758; head_lines=967; upstream_lines=792 |
|
||||
| `frontend/src/app/(app)/project/[id]/progress/page.tsx` | same path; line-set Jaccard=0.729; shared_lines=266; head_lines=346; upstream_lines=285 |
|
||||
| `frontend/src/app/(app)/project/[id]/report/page.tsx` | same path; line-set Jaccard=0.721; shared_lines=271; head_lines=362; upstream_lines=285 |
|
||||
| `frontend/src/app/(marketing)/changelog/page.tsx` | same path; line-set Jaccard=0.895; shared_lines=17; head_lines=18; upstream_lines=18 |
|
||||
| `frontend/src/app/(marketing)/contact/actions.ts` | same path; line-set Jaccard=0.949; shared_lines=37; head_lines=38; upstream_lines=38 |
|
||||
| `frontend/src/app/(marketing)/contact/nav.tsx` | same path; line-set Jaccard=0.918; shared_lines=56; head_lines=59; upstream_lines=58 |
|
||||
| `frontend/src/app/(marketing)/contact/page.tsx` | same path; line-set Jaccard=0.909; shared_lines=60; head_lines=63; upstream_lines=63 |
|
||||
| `frontend/src/app/(marketing)/file-guide/page.tsx` | same path; line-set Jaccard=0.895; shared_lines=17; head_lines=18; upstream_lines=18 |
|
||||
| `frontend/src/app/(marketing)/landing-cta.tsx` | same path; line-set Jaccard=0.865; shared_lines=45; head_lines=49; upstream_lines=48 |
|
||||
| `frontend/src/app/(marketing)/page.tsx` | same path; line-set Jaccard=0.977; shared_lines=340; head_lines=344; upstream_lines=344 |
|
||||
| `frontend/src/app/(marketing)/privacy/page.tsx` | same path; line-set Jaccard=0.895; shared_lines=17; head_lines=18; upstream_lines=18 |
|
||||
| `frontend/src/app/(marketing)/terms/page.tsx` | same path; line-set Jaccard=0.895; shared_lines=17; head_lines=18; upstream_lines=18 |
|
||||
| `frontend/src/app/opengraph-image.tsx` | same path; line-set Jaccard=0.933; shared_lines=84; head_lines=87; upstream_lines=87 |
|
||||
| `frontend/src/components/dashboard/create-project-dialog.tsx` | same path; line-set Jaccard=0.849; shared_lines=1960; head_lines=2218; upstream_lines=2050 |
|
||||
| `frontend/src/components/dashboard/onboarding-survey.tsx` | same path; line-set Jaccard=0.980; shared_lines=100; head_lines=101; upstream_lines=101 |
|
||||
| `frontend/src/components/dashboard/project-card.tsx` | same path; line-set Jaccard=0.882; shared_lines=134; head_lines=147; upstream_lines=139 |
|
||||
| `frontend/src/components/dashboard/projects-table.tsx` | same path; line-set Jaccard=0.917; shared_lines=143; head_lines=153; upstream_lines=146 |
|
||||
| `frontend/src/components/layout/sidebar.tsx` | same path; line-set Jaccard=0.685; shared_lines=185; head_lines=250; upstream_lines=205 |
|
||||
| `frontend/src/components/legal/changelog-timeline.tsx` | same path; line-set Jaccard=0.974; shared_lines=151; head_lines=153; upstream_lines=153 |
|
||||
| `frontend/src/components/legal/legal-page.tsx` | same path; line-set Jaccard=0.983; shared_lines=116; head_lines=117; upstream_lines=117 |
|
||||
| `frontend/src/components/pdf/pdf-viewer-panel.tsx` | same path; line-set Jaccard=0.992; shared_lines=257; head_lines=258; upstream_lines=258 |
|
||||
| `frontend/src/components/report/component-group.tsx` | same path; line-set Jaccard=0.911; shared_lines=82; head_lines=88; upstream_lines=84 |
|
||||
| `frontend/src/components/report/finding-card.tsx` | same path; line-set Jaccard=0.696; shared_lines=167; head_lines=231; upstream_lines=176 |
|
||||
| `frontend/src/components/report/finding-focus-view.tsx` | same path; line-set Jaccard=0.935; shared_lines=115; head_lines=122; upstream_lines=116 |
|
||||
| `frontend/src/components/report/findings-list.tsx` | same path; line-set Jaccard=0.824; shared_lines=136; head_lines=161; upstream_lines=140 |
|
||||
| `frontend/src/components/report/report-filters.tsx` | same path; line-set Jaccard=0.678; shared_lines=78; head_lines=115; upstream_lines=78 |
|
||||
| `frontend/src/components/report/report-summary.tsx` | same path; line-set Jaccard=0.872; shared_lines=68; head_lines=77; upstream_lines=69 |
|
||||
| `frontend/src/components/upload/file-upload-zone.tsx` | same path; line-set Jaccard=0.364; shared_lines=55; head_lines=143; upstream_lines=63 |
|
||||
| `frontend/src/hooks/use-optional-auth.ts` | same path; line-set Jaccard=0.198; shared_lines=24; head_lines=113; upstream_lines=32 |
|
||||
| `frontend/src/hooks/use-pipeline-progress.ts` | same path; line-set Jaccard=0.953; shared_lines=202; head_lines=212; upstream_lines=202 |
|
||||
| `frontend/src/hooks/use-report.ts` | same path; line-set Jaccard=0.263; shared_lines=15; head_lines=50; upstream_lines=22 |
|
||||
| `frontend/src/hooks/use-reviewed-count.ts` | same path; line-set Jaccard=0.688; shared_lines=22; head_lines=31; upstream_lines=23 |
|
||||
| `frontend/src/hooks/use-reviewed-findings.ts` | same path; line-set Jaccard=0.841; shared_lines=58; head_lines=67; upstream_lines=60 |
|
||||
| `frontend/src/lib/api.ts` | same path; line-set Jaccard=0.726; shared_lines=605; head_lines=816; upstream_lines=622 |
|
||||
| `frontend/src/lib/csp-hosts.ts` | same path; line-set Jaccard=0.500; shared_lines=7; head_lines=13; upstream_lines=8 |
|
||||
| `frontend/src/lib/report-export.ts` | same path; line-set Jaccard=0.860; shared_lines=49; head_lines=56; upstream_lines=50 |
|
||||
| `frontend/src/lib/site.ts` | same path; line-set Jaccard=0.895; shared_lines=51; head_lines=54; upstream_lines=54 |
|
||||
| `frontend/src/lib/types.ts` | same path; line-set Jaccard=0.570; shared_lines=241; head_lines=419; upstream_lines=245 |
|
||||
| `frontend/src/lib/version.ts` | same path; line-set Jaccard=0.333; shared_lines=2; head_lines=4; upstream_lines=4 |
|
||||
| `scripts/dedup_library_datasheets.py` | same path; line-set Jaccard=0.975; shared_lines=78; head_lines=79; upstream_lines=79 |
|
||||
| `skills/extract-pintable/schema.json` | same path; line-set Jaccard=0.522; shared_lines=35; head_lines=67; upstream_lines=35 |
|
||||
| `skills/extract-pintable/validate.py` | same path; line-set Jaccard=0.416; shared_lines=57; head_lines=137; upstream_lines=57 |
|
||||
| `taxonomy/crystal.json` | same path; line-set Jaccard=0.857; shared_lines=18; head_lines=21; upstream_lines=18 |
|
||||
| `taxonomy/passive.json` | same path; line-set Jaccard=0.955; shared_lines=42; head_lines=43; upstream_lines=43 |
|
||||
| `tests/conftest.py` | same path; line-set Jaccard=0.939; shared_lines=31; head_lines=33; upstream_lines=31 |
|
||||
| `tests/impedancefinder/__init__.py` | identical blob to upstream backend/__init__.py (rename/move) renamed_from=`backend/__init__.py` |
|
||||
| `tests/test_cost_estimator.py` | same path; line-set Jaccard=0.970; shared_lines=64; head_lines=65; upstream_lines=65 |
|
||||
| `tests/test_cost_estimator_model_aware.py` | same path; line-set Jaccard=0.530; shared_lines=70; head_lines=94; upstream_lines=108 |
|
||||
| `tests/test_datasheet_excerpt_tool.py` | same path; line-set Jaccard=0.962; shared_lines=150; head_lines=153; upstream_lines=153 |
|
||||
| `tests/test_dedupe_findings.py` | same path; line-set Jaccard=0.849; shared_lines=107; head_lines=125; upstream_lines=108 |
|
||||
| `tests/test_edif_parser.py` | same path; line-set Jaccard=0.970; shared_lines=129; head_lines=131; upstream_lines=131 |
|
||||
| `tests/test_led_current_check.py` | same path; line-set Jaccard=0.957; shared_lines=90; head_lines=92; upstream_lines=92 |
|
||||
| `tests/test_netlist_parser.py` | same path; line-set Jaccard=0.974; shared_lines=76; head_lines=77; upstream_lines=77 |
|
||||
| `tests/test_normalize_findings.py` | same path; line-set Jaccard=0.869; shared_lines=153; head_lines=175; upstream_lines=154 |
|
||||
| `tests/test_pin_mux_check.py` | same path; line-set Jaccard=0.828; shared_lines=154; head_lines=183; upstream_lines=157 |
|
||||
| `tests/test_purple_parts_resolver.py` | same path; line-set Jaccard=0.961; shared_lines=368; head_lines=375; upstream_lines=376 |
|
||||
| `tests/test_validation_concurrency.py` | same path; line-set Jaccard=0.961; shared_lines=146; head_lines=149; upstream_lines=149 |
|
||||
| `tests/test_validation_deterministic_seed.py` | same path; line-set Jaccard=0.970; shared_lines=64; head_lines=65; upstream_lines=65 |
|
||||
| `tests/test_validation_no_tool_recovery.py` | same path; line-set Jaccard=0.951; shared_lines=136; head_lines=140; upstream_lines=139 |
|
||||
| `tests/test_validation_trace_log.py` | same path; line-set Jaccard=0.961; shared_lines=172; head_lines=176; upstream_lines=175 |
|
||||
| `vendor/impedancefinder/__init__.py` | identical blob to upstream backend/__init__.py (rename/move) renamed_from=`backend/__init__.py` |
|
||||
|
||||
### REPLACEMENT (4)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
| `README.md` | same path; low overlap Jaccard=0.133; shared_lines=10; head_lines=54; upstream_lines=31 |
|
||||
| `frontend/src/components/layout/sidebar-auth.tsx` | same path; low overlap Jaccard=0.105; shared_lines=6; head_lines=55; upstream_lines=8 |
|
||||
| `frontend/src/lib/auth.ts` | same path; low overlap Jaccard=0.093; shared_lines=4; head_lines=35; upstream_lines=12 |
|
||||
| `skills/extract-pintable/SKILL.md` | same path; low overlap Jaccard=0.066; shared_lines=10; head_lines=114; upstream_lines=48 |
|
||||
|
||||
### NEW (139)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
| `backend/periscopex/antenna_geometry.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/antenna_rf.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/bom_match_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/cad_bridge.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/crystal_cl_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/dnp_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/errata_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/eval_report.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/filter_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/finding_engine.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/functional_groups.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/hf_coverage_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/impedance.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/impedance_traces.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/internal_features_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/layout_rules.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/library_gate.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/lifecycle.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/nc_pin_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/netlist_bundle.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/parsers_kicad.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/parsers_kicad_pcb.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/passive_rail_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pcb_checks.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pcb_inventory.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pcb_net_match.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pcb_power_thermal.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pcb_review.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/pdf_text.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/placement_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/placement_pack.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/power_margin_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/quote_verify.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/review_fingerprint.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/review_workflow.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/sequencing_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/si_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/periscopex/thermal_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/routers/auth.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/routers/impedance.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/datasheet_finder.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/llm/deepseek_provider.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/llm/local_skill.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/llm/pdf_ingest.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/local_jwt.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/local_users.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/mouser.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/passive_from_distributor.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/passive_from_mpn.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/passive_from_value.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/pcb_pipeline.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/pcb_validation.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/placement_pipeline.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/services/user_directory.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `backend/vendor_path.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `docker-compose.yml` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `docs/motore-finding.md` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `docs/piano-implementazione.md` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `docs/piano-pcb-review.md` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/dockerfile` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/app/(app)/library/page.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/app/(app)/project/[id]/pcb/page.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/app/(app)/project/[id]/placement/page.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/app/(marketing)/sign-in/page.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/app/(marketing)/sign-up/page.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/layout/auth-gate.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/project/impedance-panel.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/project/pcb-upload.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/project/topology-panel.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/report/finding-review-controls.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/components/report/pcb-exam-section.tsx` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/hooks/use-pcb-progress.ts` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/hooks/use-placement-progress.ts` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/lib/layout-finding.ts` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `frontend/src/lib/storage-keys.ts` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `plugins/__init__.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `plugins/kicad/__init__.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `plugins/kicad/focus.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `plugins/kicad/periscope_plugin.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `scripts/smoke_simple_project.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `scripts/update-periscope.sh` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `simple_project/eval_golden.json` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `simple_project/smoke_baseline.json` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/impedancefinder/conftest.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/impedancefinder/test_geometry.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/impedancefinder/test_net_walk.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/impedancefinder/test_planes.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/impedancefinder/test_zsolver.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_antenna_rf.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_bom_match_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_bom_parser.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_cad_bridge.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_crystal_cl_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_datasheet_finder.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_deepseek_only.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_deepseek_provider.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_derating.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_digikey_match.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_dnp_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_errata_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_eval_report.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_event_bridge.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_filter_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_finding_engine.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_finding_schema.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_functional_groups.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_hf_coverage_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_impedance.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_impedance_traces.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_internal_features_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_job_runner.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_kicad_parser.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_kicad_pcb.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_layout_rules.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_library.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_lifecycle_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_local_auth.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_nc_pin_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_netlist_bundle.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_passive_from_distributor.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_passive_from_mpn.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_passive_from_value.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_passive_rail_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_pcb_review.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_pdf_ingest.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_placement_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_placement_pack.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_placement_pipeline.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_power_margin_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_quote_verify.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_reopen_replace.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_reprocess.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_review_fingerprint.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_review_parse.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_review_workflow.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_roadmap_integrations.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_sequencing_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_si_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
| `tests/test_thermal_check.py` | path absent from upstream/main; no identical-SHA or >=0.15 Jaccard rename match |
|
||||
|
||||
### THIRD_PARTY (9)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
| `vendor/impedancefinder/SOURCE.md` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/geometry.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/model.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/net_analysis.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/net_walk.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/planes.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/report.py` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/stackup.schema.yml` | lives under vendor/; see SOURCE.md if present |
|
||||
| `vendor/impedancefinder/zsolver.py` | lives under vendor/; see SOURCE.md if present |
|
||||
|
||||
### UNKNOWN (0)
|
||||
|
||||
| Path | Evidence |
|
||||
| --- | --- |
|
||||
@@ -1,46 +0,0 @@
|
||||
# Recinto PinScope (Fase A)
|
||||
|
||||
Documented fence. **No file moves.** `LICENSE` AGPL-3.0 is unchanged. The GitHub fork `manvalan/periscope` → `Faradworks/Pinscope` is **not** detached.
|
||||
|
||||
## Branch topology
|
||||
|
||||
| Ref | Role |
|
||||
| --- | --- |
|
||||
| `origin/main` | Archival: upstream Pinscope + Docker/OSS (+3). Not the live product. |
|
||||
| `cursor/pcb-review-plan-44dd` | Live product (schema, PCB exam, finding engine). **PCB workers stay here.** |
|
||||
| `cursor/pinscope-fase-a-identity` | Fase A identity + compat fence only. Do not land engine/PCB rewrites on this branch. |
|
||||
|
||||
Merging product onto `main` is a later process step, still without detaching the fork.
|
||||
|
||||
## Inherited tree (KEEP behind the fence)
|
||||
|
||||
Treat as in-tree PinScope, not as “native Periscope”:
|
||||
|
||||
- `backend/periscopex/{graph,models,parsers*,validate,validation_tools,resolve_passives,derating,bom_summary,taxonomy,pin_mux_check,led_current_check,pin_function_tokens}`
|
||||
- `backend/services/pipeline.py`, `pipeline_worker.py`, `validation.py`, `extraction.py` (DIRECT)
|
||||
- `skills/`, `scripts/upload_skills.py`
|
||||
- Open-core Clerk/billing stubs
|
||||
- `simple_project/` fixtures, `docs/how-it-works.svg`
|
||||
|
||||
Native Periscope (do not fold into the fence): `finding_engine.py`, PCB/placement/antenna jobs, DeepSeek providers, local JWT, this identity layer.
|
||||
|
||||
## Write vs read (identifiers)
|
||||
|
||||
| Write (native) | Read-only / migrate |
|
||||
| --- | --- |
|
||||
| `periscope_token` | `pinscope_token` |
|
||||
| `periscopex:*` localStorage | `pinscopex:*` |
|
||||
| JWT `iss=periscope-local` | also accept `pinscope-local` |
|
||||
| `periscope_version` in `project.json` | also accept `pinscope_version` |
|
||||
|
||||
Code: `frontend/src/lib/pinscope-compat.ts`, `backend/pinscope_compat.py`.
|
||||
|
||||
**Do not rename** live Docker networks `pinscope_pinscope` / `pinscope_periscope` in Fase A (VPS Caddy).
|
||||
|
||||
## Identity
|
||||
|
||||
Operator of https://periscope.michelebigi.it is **Michele Bigi**. UI, TOS, privacy, metadata, and contact must not say the operator is Faradworks, Inc. Attribution to Faradworks/Pinscope remains for AGPL lineage only.
|
||||
|
||||
## Out of scope here (later phases)
|
||||
|
||||
Clerk/Anthropic runtime isolation (B). Rewrite of `validate.py` / finding engine (C). UI FindingCard (D). Parsers/graph (E). Fork detach (legal, not this plan).
|
||||
@@ -1,289 +0,0 @@
|
||||
# Piano — indipendenza architettonica e di licenza da PinScope
|
||||
|
||||
**Stato:** solo piano. Nessuna modifica a source di prodotto, remotes, `LICENSE`, né git history.
|
||||
**Base:** [PINSCOPE_AUDIT.md](./PINSCOPE_AUDIT.md) (snapshot prodotto `facbaa2` / `cursor/pcb-review-plan-44dd`).
|
||||
**Intento di Michele:** *lasciare* il codice PinScope e le sue licenze; **isolare** l’albero ereditato; disegnare dipendenze; marcare cosa **deve** essere riscritto. Non cancellare ogni riga ereditata.
|
||||
|
||||
Questo piano **non** stacca il fork GitHub (`manvalan/periscope` ← `Faradworks/Pinscope`). Lo stacco è un passo legale successivo, fuori da queste fasi di lavoro, salvo decisione esplicita.
|
||||
|
||||
---
|
||||
|
||||
## 1. Obiettivo
|
||||
|
||||
Due indipendenze distinte, nello stesso prodotto:
|
||||
|
||||
| Asse | Cosa significa | Cosa *non* significa |
|
||||
| --- | --- | --- |
|
||||
| **Architetturale** | Periscope evolve identity, auth, LLM, contratto finding, job PCB/placement **senza** fingere di essere Faradworks/Pinscope e senza che il nuovo codice importi il loop PinScope come se fosse “nostro” | Cancellare `graph.py`, i parser, o il 79% di linee lineage |
|
||||
| **Licenza / identità** | Operatore, TOS, privacy, metadata, vendor terzi con licenza **nota**; AGPL del fork **resta visibile** e attribuita | Relicenziare l’albero PinScope, togliere AGPL, o chiedere a Faradworks una licenza commerciale come se fossimo noi l’upstream |
|
||||
|
||||
**Criterio di fatto:** un modulo è “nativo Periscope” se può cambiare contratto (finding, PCB, auth locale, DeepSeek) senza patchare il recinto PinScope. Un modulo è “ereditato” se un `git blame`/Jaccard da audit lo lega a `pinscopex` / UI OSS Faradworks.
|
||||
|
||||
L’audit già dice: l’indipendenza **non** è bloccata dalle stringhe `pinscope`; è bloccata da pipeline, identità legale, `main` stantio, seam Clerk/Anthropic, ImpedenceFinder senza licenza.
|
||||
|
||||
---
|
||||
|
||||
## 2. Confine di isolamento (fence)
|
||||
|
||||
### 2.1 Recinto — albero ereditato (resta, non si “ripulisce”)
|
||||
|
||||
Trattare come **una dipendenza in-tree**, non come prodotto Periscope:
|
||||
|
||||
| Pacchetto logico | Path odierni | Licenza da audit |
|
||||
| --- | --- | --- |
|
||||
| Core schematico PinScope | `backend/periscopex/{graph,models,parsers,parsers_edif,validate,validation_tools,resolve_passives,derating,bom_summary,taxonomy,pin_mux_check,led_current_check,pin_function_tokens}.py` | AGPL-3.0 del fork (blob `LICENSE` identico a upstream) |
|
||||
| Orchestrazione review | `backend/services/pipeline.py`, `pipeline_worker.py`, `services/validation.py`, `services/extraction.py` (parte DIRECT) | stessa |
|
||||
| Skills Anthropic/Console | `skills/extract-pattern`, `skills/extract-specs`, `skills/extract-pintable/{schema.json,validate.py}`, `scripts/upload_skills.py`, `backend/skills_manifest.json` | stessa + contratto Claude |
|
||||
| UI OSS / marketing shell | gran parte di `frontend/src` UPSTREAM/DERIVED, `frontend/public`, `frontend/content/terms.md` + `privacy.md` (testo Faradworks) | AGPL + TOS Faradworks **nel contenuto** |
|
||||
| Gateway seams | `billing_hook.py`, `proxy.ts`, `clerk-theme-provider.tsx`, `use-optional-auth.ts`, analytics, CSP hosts | stub open-core PinScope |
|
||||
| Fixture upstream | `simple_project/` (asc/csv/pdf/`design_graph.json`), `docs/how-it-works.svg` | AGPL / contenuto upstream |
|
||||
|
||||
**Regola del recinto (quando si implementerà, non ora):**
|
||||
|
||||
- Import **solo** da una facciata stretta (es. `build_graph`, `parse_bom`, `parse_netlist_any`, `Finding` Pydantic, `validate_design_async`).
|
||||
- Nessun nuovo check PCB/schema scrive dentro `validate.py`.
|
||||
- Nessun file nativo assume Clerk, Anthropic Console, `pinscope.ai`, Faradworks Inc.
|
||||
- Il recinto **conserva** `LICENSE` AGPL e l’attribuzione “derived from Faradworks/Pinscope”.
|
||||
|
||||
Implementazione fisica futura (non in fase identità): directory tipo `vendor/pinscope/` **oppure** package `backend/pinscope_legacy/` con shim `periscopex` — solo dopo che i consumer nativi non importano più path sparsi. Non è un rename cosmetico (`pinscopex` → `periscopex` è già successo e **non** è un recinto).
|
||||
|
||||
### 2.2 Nativo Periscope (fuori recinto)
|
||||
|
||||
| Area | Path | Perché è nativo |
|
||||
| --- | --- | --- |
|
||||
| Contratto finding | `finding_engine.py`, `docs/motore-finding.md` | NEW; clamp FACT/REQUIREMENT/INFERENCE |
|
||||
| PCB exam | `pcb_pipeline.py`, `pcb_*.py`, `parsers_kicad_pcb.py`, UI `pcb/` | NEW; INDIRECT sul grafo |
|
||||
| Placement F2 | `placement_pipeline.py`, `placement_pack.py`, `functional_groups.py` | NEW |
|
||||
| RF / antenna | `antenna_*.py`, parte Impedance UI | NEW |
|
||||
| Facade Z0 | `periscopex/impedance.py` | NEW; il solver è terzi |
|
||||
| Auth self-host | `local_jwt.py`, `local_users.py`, `routers/auth.py` | REPLACEMENT |
|
||||
| LLM DeepSeek | `deepseek_provider.py`, `local_skill.py`, `pdf_ingest.py` | NEW |
|
||||
| Deploy | `scripts/update-periscope.sh`, `docker-compose.yml` | NEW (nomi `pinscope_*` ancora WEAK) |
|
||||
| Plugin KiCad | `plugins/kicad/` | NEW |
|
||||
| Check deterministici fork | `dnp_check`, `sequencing_check`, `layout_rules`, … | NEW ma **INDIRECT**: usano `models` / graph |
|
||||
|
||||
### 2.3 Terzi (né PinScope né “nostro motore”)
|
||||
|
||||
`vendor/impedancefinder/` — commit `a0c8d0ec`, licenza **UNKNOWN** (audit §6.4). Resta dietro `impedance.py` + `vendor_path.py`. Non mescolare con AGPL PinScope né con il finding engine.
|
||||
|
||||
---
|
||||
|
||||
## 3. Albero delle dipendenze
|
||||
|
||||
Forza: **DIRECT** = stesso control flow / blob lineage PinScope; **INDIRECT** = codice nativo che importa DIRECT; **WEAK** = alias, brand, reti Docker.
|
||||
|
||||
```
|
||||
Faradworks/Pinscope (AGPL-3.0)
|
||||
│
|
||||
├─ DIRECT periscopex.graph / parsers* / taxonomy / models
|
||||
│ ├─ INDIRECT pipeline MODE=run (Parse → extract → build_graph → review)
|
||||
│ ├─ INDIRECT pcb_pipeline.ensure_graph + pintable cache
|
||||
│ ├─ INDIRECT placement / functional_groups
|
||||
│ └─ INDIRECT cad_bridge, parsers_kicad*, pcb_inventory
|
||||
│
|
||||
├─ DIRECT validate.py + validation_tools + extraction skills
|
||||
│ ├─ INDIRECT finding_engine.complete_findings (graft, non sostituisce il loop)
|
||||
│ ├─ INDIRECT pcb AI (_parse_review)
|
||||
│ └─ WEAK anthropic SDK, upload_skills.py, CLAUDE.md Console
|
||||
│
|
||||
├─ DIRECT models.Finding + normalize_findings + dedupe_findings
|
||||
│ └─ INDIRECT finding_engine (PE-*, provenance clamp) + FindingCard
|
||||
│
|
||||
├─ DIRECT Next.js app (wizard, report, admin, legal pages)
|
||||
│ ├─ INDIRECT pcb-exam-section, impedance-panel, topology-panel, local sign-in
|
||||
│ └─ WEAK Faradworks metadata, pinscopex localStorage, ClerkThemeProvider
|
||||
│
|
||||
├─ DIRECT Clerk / billing_hook seams
|
||||
│ └─ INDIRECT local JWT (parallel stack, non ha spento Clerk)
|
||||
│
|
||||
└─ (non PinScope) vendor/impedancefinder
|
||||
└─ INDIRECT periscopex.impedance → tab RF
|
||||
```
|
||||
|
||||
### 3.1 Consumer Periscope × sottosistema PinScope
|
||||
|
||||
| Sottosistema PinScope | Consumer Periscope | Forza | Se si riscrive il PinScope… |
|
||||
| --- | --- | --- | --- |
|
||||
| Graph + parsers BOM/netlist | `pipeline.py`, PCB, placement, tutti i check | DIRECT / INDIRECT | Cade tutto il prodotto |
|
||||
| `validate.py` review loop | report schema, PCB AI | DIRECT | Serve un reviewer nativo **prima** di spegnere PinScope |
|
||||
| `Finding` Pydantic | `finding_engine`, UI card, cad-bridge | DIRECT | Mappare al contratto `motore-finding.md` |
|
||||
| Skills pintable/pattern/specs | extraction, library | DIRECT | Già in parte sostituiti da `local_skill.py` |
|
||||
| UI shell | ogni pagina | DIRECT | Identità e finding card prima; wizard dopo |
|
||||
| Clerk/billing | `main.py`, admin, email | DIRECT + WEAK | Auth locale già c’è |
|
||||
| Legal Faradworks | `/terms` `/privacy` layout | DIRECT contenuto | Non è codice motore |
|
||||
|
||||
**Cosa deve essere riscritto** (per indipendenza vera, fasi tarde — non per “pulizia”):
|
||||
|
||||
1. **Identità legale/UI** — obbligatorio e **primo**; non è il motore.
|
||||
2. **Seam Clerk + Anthropic** — obbligatorio per non dipendere dal cloud Faradworks.
|
||||
3. **Loop `validate.py` / `_parse_review` / tool graph** — obbligatorio per un motore *Periscope*, **dopo** identità; oggi `finding_engine` è solo un clamp.
|
||||
4. **IR `Finding`** — allineare il tipo Pydantic al contratto FACT/REQUIREMENT/INFERENCE (oggi campi doppi `finding`/`why`).
|
||||
5. **UI report/wizard** — quando il contratto finding è nativo; non nella fase identità.
|
||||
6. **Parsers/graph** — *non* obbligatori per identità; rewrite solo se si vuole un CAD layer Periscope senza AGPL PinScope (programma XL, ultimo).
|
||||
|
||||
---
|
||||
|
||||
## 4. Mappa licenze — cosa blocca una licenza Periscope “pulita”
|
||||
|
||||
“Pulita” = *Periscope come opera dell’operatore Michele, con dipendenze dichiarate*, **non** “Periscope senza AGPL”. L’albero PinScope **resta AGPL**; toglierlo richiederebbe rewrite totale del recinto (fase ultima) **e** comunque rispetto dei diritti sul codice già pubblicato.
|
||||
|
||||
| Pezzo | Evidenza audit | Blocca identità Periscope? | Blocca relicenza non-AGPL del binario combinato? |
|
||||
| --- | --- | --- | --- |
|
||||
| `LICENSE` AGPL-3.0 identico a upstream | KEEP | No, se attribuito | **Sì**, finché il recinto è nel tree/servizio |
|
||||
| TOS/privacy Faradworks, Inc. + `dev@faradworks.com` | REPLACE | **Sì** (operatore sbagliato) | No (non è la GPL) |
|
||||
| Metadata `layout.tsx` authors/publisher Faradworks, `@getFaradWorks` | REPLACE | **Sì** | No |
|
||||
| README “commercial licensing of the original → Faradworks” | tenere come *attribuzione upstream*, non come offerta nostra | Parziale | No |
|
||||
| Fork GitHub ancora `parent: Faradworks/Pinscope` | non staccare in questo piano | No (fatto) | No |
|
||||
| `anthropic` in `requirements.txt` + `upload_skills.py` | ISOLATE | Policy prodotto / ToS Anthropic vs DeepSeek | No |
|
||||
| Clerk JWKS / API | ISOLATE | ToS Clerk se usato in prod | No |
|
||||
| ImpedenceFinder `license: null`, no LICENSE file | INVESTIGATE | Distribuzione RF tab | **Sì** per *quel* vendor (sconosciuta) |
|
||||
| PyMuPDF dual AGPL *or* Artifex commercial | INVESTIGATE | Quale grant in VPS | AGPL già dal root; commerciale Artifex è altro contratto |
|
||||
| Transitive Python | no lockfile | Audit incompleto | Possibile gap |
|
||||
| npm MPL/LGPL (lightningcss, sharp-libvips) | lock fields | Attribuzione frontend | Copyleft diverso, non Faradworks |
|
||||
| `xlsx@0.18.5` Apache-2.0 nel lock | OK dichiarato | No | No |
|
||||
|
||||
**Bloccanti identità (fase A):** testi Faradworks nel prodotto.
|
||||
**Bloccanti “SaaS Periscope senza AGPL”:** l’intero recinto PinScope + PyMuPDF se si usa il grant AGPL. **Questo piano non promette relicenza.**
|
||||
**Bloccante vendor:** ImpedenceFinder senza licenza — non distribuire come “Periscope MIT/Apache”.
|
||||
|
||||
---
|
||||
|
||||
## 5. KEEP / ISOLATE / REWRITE / REMOVE
|
||||
|
||||
Sforzo: **S** (file/config), **M** (un sottosistema, test esistenti), **L** (contratto + UI + pipeline), **XL** (nuovo CAD/IR). Non è calendario.
|
||||
|
||||
| Area | Azione | Sforzo | Fase |
|
||||
| --- | --- | --- | --- |
|
||||
| Blob `LICENSE` AGPL + attribuzione PinScope | **KEEP** | — | tutte |
|
||||
| Graph, parsers, taxonomy, BOM, derating, EDIF | **KEEP** nel recinto | — | A–C; **REWRITE** solo E (XL) |
|
||||
| `simple_project`, how-it-works.svg | **KEEP** | — | |
|
||||
| Changelog storico Pinscope/`PS-*` | **KEEP** | — | |
|
||||
| PCB, placement, antenna, plugin, `finding_engine` come clamp | **KEEP** nativo | — | non toccare in A |
|
||||
| TOS, privacy, contact, footer ©, `layout.tsx` authors, Twitter | **REWRITE** (identità) | S–M | **A** |
|
||||
| `storage-keys.ts` write `pinscopex:` | **REWRITE** write path; dual-read | S | A |
|
||||
| Alias `pinscope_version`, JWT `pinscope-local`, CORS host, Docker `pinscope_*` | **ISOLATE** poi scadere | S | A |
|
||||
| Clerk stack | **ISOLATE**; **REMOVE** quando JWT locale è l’unico modo | M | B |
|
||||
| `anthropic`, `upload_skills.py`, CLAUDE.md Console | **ISOLATE**; **REMOVE** se DeepSeek-only | M | B |
|
||||
| `billing_hook` / proxy stubs | **ISOLATE** (firme); non Faradworks in copy | S | B |
|
||||
| `validate.py` + `validation_tools` + `_parse_review` | **KEEP** in A–B; **REWRITE** reviewer nativo | L | **C** |
|
||||
| `models.Finding` vs `motore-finding.md` | **REWRITE** IR (compat shim) | M–L | C |
|
||||
| `normalize_findings` / `dedupe_findings` | **KEEP** o reimplementare sul nuovo IR | M | C |
|
||||
| Skills extraction (schema) | **ISOLATE**; prompt già in parte REPLACEMENT | M | C |
|
||||
| FindingCard / report UI | **REWRITE** quando IR nativo | M–L | **D** |
|
||||
| Wizard upload / admin Clerkisms | **REWRITE** / pulizia | M | D |
|
||||
| `vendor/impedancefinder` | **ISOLATE** + licenza esplicita | S legale + M se drop | B (licenza), non engine |
|
||||
| Parsers/graph nativi Periscope | **REWRITE** solo se si esce da AGPL PinScope | XL | **E** |
|
||||
| Stacco fork GitHub | **non in questo piano** | legale | dopo E o mai |
|
||||
|
||||
**REMOVE** in A: niente codice motore. Solo copy Faradworks *dopo* TOS Periscope pronti.
|
||||
|
||||
---
|
||||
|
||||
## 6. Sequenza per fasi
|
||||
|
||||
### Fase A — Identità e recinto documentale (**non** rewriting del finding engine)
|
||||
|
||||
Allineato all’audit §11 punti 1–4: `finding_engine` / `validate.py` **non** si riscrivono qui.
|
||||
|
||||
1. Pubblicare audit + questo piano (solo docs).
|
||||
2. Decidere `origin/main` vs `cursor/pcb-review-plan-44dd` **senza** staccare il fork (merge prodotto su `main` *oppure* `main` dichiarato archivio).
|
||||
3. TOS/privacy/contact/metadata/Twitter/footer operatore Periscope; una riga di attribuzione AGPL PinScope/Faradworks (non “siamo Faradworks”).
|
||||
4. Compatibility fence: un modulo/doc per chiavi `pinscope_*`; smettere di **scrivere** `pinscopex:` in `localStorage`.
|
||||
5. Dichiarare a codice (commento/ADR) il recinto §2 — ancora senza muovere file.
|
||||
|
||||
**Done quando:** UI e legal non dicono Faradworks Inc.; AGPL e fork parent invariati; PCB/schema invariati.
|
||||
|
||||
### Fase B — Seam runtime (Clerk, Anthropic, vendor license)
|
||||
|
||||
Audit §11 punti 5–6, 8–9.
|
||||
|
||||
- JWT locale default documentato; Clerk dietro seam nominato (`use_clerk`).
|
||||
- DeepSeek unico provider supportato; Anthropic + Console skills = optional/legacy; CLAUDE.md = README.
|
||||
- Licenza scritta per ImpedenceFinder **o** non distribuire `vendor/`.
|
||||
- Lockfile Python per transitive.
|
||||
|
||||
**Done quando:** boot produzione non richiede Clerk; nessun path “devi uploadare skill Anthropic”; vendor RF ha SPDX o è fuori tree.
|
||||
|
||||
### Fase C — Motore review PinScope-derived (**rewrite map**, dopo A–B)
|
||||
|
||||
Qui sì si riscrive l’engine ereditato. Ordine interno:
|
||||
|
||||
| Step | Oggetto | Perché |
|
||||
| --- | --- | --- |
|
||||
| C0 | Spec freeze `motore-finding.md` = unico IR | Già nativo; non diluire in `validate.py` |
|
||||
| C1 | Adapter: `validate.py` emette solo `Finding` grezzi → `complete_finding` | Già parziale; chiudere i campi doppi |
|
||||
| C2 | **REWRITE** loop per-IC: tool `find_connected_components` / `get_pintable` / excerpt su provider DeepSeek **senza** copiare `validation_tools.py` | Cuore DIRECT da sostituire |
|
||||
| C3 | Extraction: `local_skill.py` + schemi JSON (KEEP schema se identici; REWRITE orchestrazione Anthropic) | |
|
||||
| C4 | Spegnere import da `validate.py` nel PCB (`_parse_review` → parser finding nativo) | Toglie INDIRECT PCB→PinScope reviewer |
|
||||
| C5 | Test golden `simple_project` + Emmaforo: parity FACT/REQUIREMENT, non parity prose | |
|
||||
|
||||
**Non in C:** riscrivere `graph.py` / parser KiCad.
|
||||
|
||||
**Done quando:** `validate.py` PinScope può restare nel recinto **non chiamato** dal job schema/PCB, oppure ridotto a wrapper deprecato.
|
||||
|
||||
### Fase D — UI
|
||||
|
||||
FindingCard, filtri Layout, wizard, admin senza Clerk copy, `package-lock` version drift. Dopo C1 almeno (stesso oggetto finding).
|
||||
|
||||
### Fase E — Parsers / graph (opzionale, XL)
|
||||
|
||||
Solo se l’obiettivo diventa *opera senza codice AGPL PinScope*. Sostituire BOM/netlist/graph/taxonomy è un secondo prodotto. PCB nativo resta INDIRECT finché `ensure_graph` chiama PinScope.
|
||||
|
||||
**Stacco fork GitHub:** solo dopo consiglio legale, **non** in A–D.
|
||||
|
||||
---
|
||||
|
||||
## 7. Rischi e sconosciuti
|
||||
|
||||
| Rischio | Impatto |
|
||||
| --- | --- |
|
||||
| Clone di default `main` ≠ prodotto 2.32.1 | Ogni PR “solo docs” su `main` descrive un altro tree |
|
||||
| Rewrite `validate.py` in A | Rompe schema+PCB; l’audit lo vieta come primo passo |
|
||||
| Recinto solo rename di cartella | Falso recinto (già successo con `periscopex`) |
|
||||
| TOS Periscope scritti male | Resta il testo Faradworks *o* si perde AGPL attribution |
|
||||
| ImpedenceFinder | Distribuzione RF senza grant |
|
||||
| PyMuPDF grant in VPS | Dual license; non verificato a runtime |
|
||||
| Clerk rimasto in `ENVIRONMENT=production` | Deploy script vs JWT locale |
|
||||
| Anthropic ancora importabile se c’è la key | README ≠ runtime |
|
||||
| Trademark Pinscope/Periscope/Faradworks | Audit: solo *use*, nessuna registrazione |
|
||||
| Relicenza “Periscope MIT” | Impossibile finché il recinto è servito (AGPL §13 network) |
|
||||
| Gateway privato Faradworks | Non è nel repo; non auditabile |
|
||||
|
||||
---
|
||||
|
||||
## 8. Non-obiettivi (espliciti)
|
||||
|
||||
- Non modificare source prodotto, remotes, `LICENSE`, git history **in questa consegna** (piano only).
|
||||
- Non staccare il fork GitHub in queste fasi (**passo legale successivo**, se mai).
|
||||
- Non cancellare il codice PinScope “perché è PinScope”.
|
||||
- Non riscrivere `finding_engine` / `validate.py` / PCB nella **fase A**.
|
||||
- Non auto-placer, non write-back pcbnew (già fuori da `piano-pcb-review.md`).
|
||||
- Non promettere una licenza Periscope non-AGPL.
|
||||
- Non mescolare mbparks/pinscope (Arduino) — progetto altro.
|
||||
- Non usare questo piano come licenza a refactor su `cursor/pcb-review-plan-44dd` oltre un eventuale **solo** file docs su **nuovo** branch.
|
||||
|
||||
---
|
||||
|
||||
## 9. Priorità di rewrite (lista operativa)
|
||||
|
||||
1. **Identità** — TOS, privacy, contact, `layout.tsx`, `site.ts` Twitter, footer ©
|
||||
2. **Fence compat** — stop write `pinscopex:`; isolare alias versione/JWT/CORS/Docker
|
||||
3. **`main` vs product branch** — topologia git, fork attaccato
|
||||
4. **Clerk seam** — default JWT locale
|
||||
5. **Anthropic/Console seam** — DeepSeek-only di fatto
|
||||
6. **Licenza ImpedenceFinder** — grant o drop vendor
|
||||
7. **Lockfile Python** — transitive
|
||||
8. **IR finding** — `Finding` ≡ `motore-finding.md` (fase C, non A)
|
||||
9. **Loop `validate.py` / `validation_tools`** — reviewer nativo (C)
|
||||
10. **PCB/schema smettono di chiamare `_parse_review` PinScope** (C4)
|
||||
11. **UI FindingCard/wizard** (D)
|
||||
12. **Parsers/graph** solo se si vuole uscire da AGPL PinScope (E)
|
||||
13. **Stacco fork** — legale, fuori piano
|
||||
|
||||
---
|
||||
|
||||
## 10. Riferimenti
|
||||
|
||||
- Audit: [PINSCOPE_AUDIT.md](./PINSCOPE_AUDIT.md)
|
||||
- Contratto finding nativo: [`docs/motore-finding.md`](../motore-finding.md) (store) / `docs/motore-finding.md` nel repo prodotto
|
||||
- Job PCB: [`docs/piano-pcb-review.md`](../piano-pcb-review.md)
|
||||
@@ -1,121 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 560" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif">
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 9 5 L 0 9 z" fill="#56637A"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- panel -->
|
||||
<rect x="0.5" y="0.5" width="1199" height="559" rx="16" fill="#0B0E14" stroke="#263041"/>
|
||||
|
||||
<!-- column headers -->
|
||||
<text x="138" y="64" fill="#7D8590" font-size="11" letter-spacing="2.5" text-anchor="middle">INPUTS</text>
|
||||
<text x="400" y="64" fill="#7D8590" font-size="11" letter-spacing="2.5" text-anchor="middle">PARSE & EXTRACT</text>
|
||||
<text x="724" y="64" fill="#7D8590" font-size="11" letter-spacing="2.5" text-anchor="middle">REVIEW</text>
|
||||
<text x="1050" y="64" fill="#7D8590" font-size="11" letter-spacing="2.5" text-anchor="middle">REPORT</text>
|
||||
|
||||
<!-- input chips -->
|
||||
<g>
|
||||
<rect x="48" y="88" width="180" height="52" rx="9" fill="#111725" stroke="#2C3850"/>
|
||||
<text x="66" y="110" fill="#E6EDF3" font-size="14" font-weight="600">netlist</text>
|
||||
<text x="66" y="128" fill="#9BA7B4" font-size="11" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">.asc · .edn</text>
|
||||
|
||||
<rect x="48" y="156" width="180" height="52" rx="9" fill="#111725" stroke="#2C3850"/>
|
||||
<text x="66" y="178" fill="#E6EDF3" font-size="14" font-weight="600">BOM</text>
|
||||
<text x="66" y="196" fill="#9BA7B4" font-size="11" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">.csv · .xlsx</text>
|
||||
|
||||
<rect x="48" y="300" width="180" height="52" rx="9" fill="#111725" stroke="#2C3850"/>
|
||||
<text x="66" y="322" fill="#E6EDF3" font-size="14" font-weight="600">datasheets</text>
|
||||
<text x="66" y="340" fill="#9BA7B4" font-size="11" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">.pdf</text>
|
||||
</g>
|
||||
|
||||
<!-- design graph box (deterministic / blue) -->
|
||||
<g>
|
||||
<rect x="300" y="88" width="200" height="106" rx="10" fill="#0E1626" stroke="#3B82F6"/>
|
||||
<text x="318" y="116" fill="#E6EDF3" font-size="15" font-weight="600">design graph</text>
|
||||
<text x="318" y="140" fill="#9BA7B4" font-size="12">components ⇄ nets,</text>
|
||||
<text x="318" y="157" fill="#9BA7B4" font-size="12">every pin resolvable,</text>
|
||||
<text x="318" y="174" fill="#9BA7B4" font-size="12">queryable</text>
|
||||
</g>
|
||||
|
||||
<!-- extraction box (model / violet) -->
|
||||
<g>
|
||||
<rect x="300" y="272" width="200" height="124" rx="10" fill="#131126" stroke="#A78BFA"/>
|
||||
<text x="318" y="300" fill="#E6EDF3" font-size="15" font-weight="600">pin tables + specs</text>
|
||||
<text x="318" y="324" fill="#9BA7B4" font-size="12">PDFs page-trimmed,</text>
|
||||
<text x="318" y="341" fill="#9BA7B4" font-size="12">read once per part,</text>
|
||||
<text x="318" y="358" fill="#9BA7B4" font-size="12">cached in a shared</text>
|
||||
<text x="318" y="375" fill="#9BA7B4" font-size="12">library</text>
|
||||
</g>
|
||||
|
||||
<!-- review box (model / violet) -->
|
||||
<g>
|
||||
<rect x="584" y="88" width="280" height="308" rx="10" fill="#131126" stroke="#A78BFA"/>
|
||||
<text x="604" y="118" fill="#E6EDF3" font-size="15" font-weight="600">per-IC review</text>
|
||||
<text x="604" y="144" fill="#9BA7B4" font-size="12">The model reads the trimmed</text>
|
||||
<text x="604" y="161" fill="#9BA7B4" font-size="12">datasheet next to that IC's circuit</text>
|
||||
<text x="604" y="178" fill="#9BA7B4" font-size="12">neighborhood, and checks it against</text>
|
||||
<text x="604" y="195" fill="#9BA7B4" font-size="12">the reference application, pin</text>
|
||||
<text x="604" y="212" fill="#9BA7B4" font-size="12">functions and abs-max ratings.</text>
|
||||
|
||||
<text x="604" y="244" fill="#7D8590" font-size="10.5" letter-spacing="1.5">TOOLS</text>
|
||||
<g font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="11" fill="#C9B8F5">
|
||||
<rect x="604" y="254" width="112" height="22" rx="5" fill="#1B1730" stroke="#3D3560"/>
|
||||
<text x="612" y="269">get_pintable</text>
|
||||
<rect x="724" y="254" width="120" height="22" rx="5" fill="#1B1730" stroke="#3D3560"/>
|
||||
<text x="732" y="269">get_net_for_pin</text>
|
||||
<rect x="604" y="282" width="196" height="22" rx="5" fill="#1B1730" stroke="#3D3560"/>
|
||||
<text x="612" y="297">find_connected_components</text>
|
||||
<rect x="604" y="310" width="176" height="22" rx="5" fill="#1B1730" stroke="#3D3560"/>
|
||||
<text x="612" y="325">get_datasheet_excerpt</text>
|
||||
</g>
|
||||
<text x="604" y="356" fill="#9BA7B4" font-size="11.5">One IC per pass — a bad response</text>
|
||||
<text x="604" y="372" fill="#9BA7B4" font-size="11.5">skips that part, never the run.</text>
|
||||
</g>
|
||||
|
||||
<!-- findings box -->
|
||||
<g>
|
||||
<rect x="948" y="88" width="204" height="130" rx="10" fill="#131126" stroke="#A78BFA"/>
|
||||
<text x="966" y="116" fill="#E6EDF3" font-size="15" font-weight="600">findings</text>
|
||||
<text x="966" y="140" fill="#9BA7B4" font-size="12">severity, reasoning,</text>
|
||||
<text x="966" y="157" fill="#9BA7B4" font-size="12">suggested fix — each</text>
|
||||
<text x="966" y="174" fill="#9BA7B4" font-size="12">cited to the datasheet</text>
|
||||
<text x="966" y="191" fill="#9BA7B4" font-size="12">page it came from</text>
|
||||
</g>
|
||||
|
||||
<!-- deterministic outputs box -->
|
||||
<g>
|
||||
<rect x="948" y="430" width="204" height="90" rx="10" fill="#0E1626" stroke="#3B82F6"/>
|
||||
<text x="966" y="458" fill="#E6EDF3" font-size="15" font-weight="600">computed, no model</text>
|
||||
<text x="966" y="482" fill="#9BA7B4" font-size="12">capacitor derating table,</text>
|
||||
<text x="966" y="499" fill="#9BA7B4" font-size="12">BOM roll-up</text>
|
||||
</g>
|
||||
|
||||
<!-- arrows -->
|
||||
<g stroke="#56637A" stroke-width="1.5" fill="none">
|
||||
<!-- netlist + BOM -> graph -->
|
||||
<path d="M 228 114 C 262 114, 264 122, 296 128" marker-end="url(#arrow)"/>
|
||||
<path d="M 228 182 C 262 182, 264 162, 296 152" marker-end="url(#arrow)"/>
|
||||
<!-- datasheets -> extraction -->
|
||||
<path d="M 228 326 C 254 326, 262 330, 296 332" marker-end="url(#arrow)"/>
|
||||
<!-- graph -> review -->
|
||||
<path d="M 500 140 C 540 140, 542 150, 580 156" marker-end="url(#arrow)"/>
|
||||
<!-- extraction -> review -->
|
||||
<path d="M 500 332 C 540 332, 542 326, 580 322" marker-end="url(#arrow)"/>
|
||||
<!-- review -> findings -->
|
||||
<path d="M 864 152 C 906 152, 906 152, 944 152" marker-end="url(#arrow)"/>
|
||||
<!-- graph -> deterministic outputs (bypasses review) -->
|
||||
<path d="M 460 194 C 460 240, 542 216, 542 300 L 542 448 C 542 474, 560 475, 600 475 L 900 475 C 924 475, 926 475, 944 475" marker-end="url(#arrow)"/>
|
||||
</g>
|
||||
|
||||
<!-- bypass label -->
|
||||
<text x="560" y="464" fill="#7D8590" font-size="10.5" font-style="italic">straight from the graph</text>
|
||||
|
||||
<!-- legend -->
|
||||
<g>
|
||||
<rect x="48" y="500" width="12" height="12" rx="3" fill="#0E1626" stroke="#3B82F6"/>
|
||||
<text x="68" y="510" fill="#9BA7B4" font-size="12">deterministic</text>
|
||||
<rect x="162" y="500" width="12" height="12" rx="3" fill="#131126" stroke="#A78BFA"/>
|
||||
<text x="182" y="510" fill="#9BA7B4" font-size="12">model-assisted</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.9 KiB |
@@ -1,120 +0,0 @@
|
||||
# Motore finding — schema e PCB (stesso oggetto)
|
||||
|
||||
Fonte: specifica di Michele (Project chat). Vale per **analizzatore schematico e PCB**. Un finding è un oggetto riproducibile, non un paragrafo LLM.
|
||||
|
||||
Libreria datasheet: **una** (`library/extracted` + extracted/ di progetto). L’esame PDF profondo gira **una volta** (pipeline schema). Il job PCB consuma quella cache e aggiunge solo geometria.
|
||||
|
||||
---
|
||||
|
||||
## 1. Tre strati — non mescolare
|
||||
|
||||
Ogni finding ha **tre campi distinti**:
|
||||
|
||||
| Strato | Nome campo | Chi lo produce | Cosa può contenere |
|
||||
| --- | --- | --- | --- |
|
||||
| **FACT** | `facts` | Motore deterministico (netlist, BOM, `.kicad_pcb`) | Solo osservabili: pin, net, mm, V, A, width, thickness |
|
||||
| **REQUIREMENT** | `requirement` | Rule DB + estrazione libreria (citazione, pagina, MPN) | Testo **sourced**. Provenance nel DB, non nella prosa del modello |
|
||||
| **INFERENCE** | `inference` | Ingegnere / LLM **solo** qui | Giudizio. Mai copiare un’inferenza in `facts` o `requirement` |
|
||||
|
||||
Non mescolare: un check non scrive “probabilmente sottodimensionato” dentro `facts`. Se manca un parametro, `evidence_status=INSUFFICIENT` e si **dice** che manca — non si inventa 1 oz, 10 °C, 50 Ω, IEC.
|
||||
|
||||
`finding` / `why` restano per compatibilità UI (copia di facts / requirement).
|
||||
|
||||
---
|
||||
|
||||
## 2. Provenance datasheet (rule DB)
|
||||
|
||||
Sul **record di regola**, non nel testo LLM:
|
||||
|
||||
| `provenance` | Significato | Classe / severity |
|
||||
| --- | --- | --- |
|
||||
| **MANDATORY** | Shall / abs-max / pin NC | Può essere **RULE** + ERROR |
|
||||
| **RECOMMENDED** | Should / layout note | **Mai ERROR**. RISK o INFO |
|
||||
| **TYPICAL** | Typical / default application | INFO o RISK, mai RULE ERROR |
|
||||
| **EXAMPLE** | Figure / typical application circuit | REVIEW o INFO |
|
||||
|
||||
**Recommended ≠ ERROR.** Il clamp è nel motore (`complete_finding`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Classi — Design Rule ≠ Design Review
|
||||
|
||||
| `finding_class` | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| **RULE** | Violazione di un requisito MANDATORY misurato (FACT vs REQUIREMENT). Deterministico. |
|
||||
| **RISK** | Margine / derating / inferenza ingegneristica con evidenza parziale. |
|
||||
| **REVIEW** | Esame AI (schema o PCB). Non è una design rule. |
|
||||
| **INFO** | Inventario, evidenza insufficiente, decisione del progettista, skip onesto. |
|
||||
|
||||
LLM `source=review` / `pcb_review` → sempre **REVIEW**, mai RULE.
|
||||
|
||||
---
|
||||
|
||||
## 4. Intent + memoria DECISION
|
||||
|
||||
Il progettista può dichiarare un intent (es. **PSEL tied low on purpose**). Persistenza: `decisions.json` nel progetto, fingerprint:
|
||||
|
||||
`{rule_id\|source}|{designator}|{net}|{aspect}`
|
||||
|
||||
`wontfix` / `false_positive` sul report **scrive** una DECISION. Al rescan il motore **non ri-naga** come ERROR: finding `suppressed=true`, `finding_class=INFO`, `decision_id` valorizzato.
|
||||
|
||||
---
|
||||
|
||||
## 5. Severity, confidence, evidence — indipendenti
|
||||
|
||||
- `status` (severity): ERROR | WARNING | INFO
|
||||
- `confidence`: 0–1 (quanto è solido il FACT+REQUIREMENT)
|
||||
- `evidence_status`: SUFFICIENT | INSUFFICIENT
|
||||
|
||||
Alto confidence + INFO è legale (misura chiara, non è un fault). Bassa confidence + ERROR **non** è legale se evidence è INSUFFICIENT: si declassa.
|
||||
|
||||
---
|
||||
|
||||
## 6. Oggetto finding (riproducibile)
|
||||
|
||||
Campi obbligatori del motore (oltre a designator / MPN / ids esistenti):
|
||||
|
||||
```
|
||||
facts, requirement, inference
|
||||
source (pagina/MPN/rule_id)
|
||||
calculation
|
||||
assumptions[]
|
||||
confidence
|
||||
status (severity)
|
||||
action # cosa fare; UI usa anche recommendation
|
||||
provenance
|
||||
finding_class
|
||||
evidence_status
|
||||
decision_id? suppressed?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Libreria unica / esame unico
|
||||
|
||||
| Store | Path |
|
||||
| --- | --- |
|
||||
| Estrazioni IC | `library/extracted/{safe_mpn}.json` (shared) ∪ `extracted/` progetto |
|
||||
| PDF blob | `library/datasheets/blobs/{md5}.pdf` |
|
||||
|
||||
PCB: `review_ic_async(..., pdf_path=None)` se c’è pintable. Skip: `no library extraction — run schematic review first`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Cosa è solo PCB
|
||||
|
||||
Geometria board: width/thickness, via, pour courtyard, placement mm, return/stitch, pad-net vs schema (`PE-LAY-*` gerarchia `/net`). **Non** duplicare check schema (mux, LED, filtri netlist).
|
||||
|
||||
---
|
||||
|
||||
## 9. Fasi (non fingere completezza)
|
||||
|
||||
**Fase A:** oggetto finding + clamp provenance/class (**ERROR solo se RULE e MANDATORY**) + decisions + UI schema/PCB + libreria condivisa + check geometria già gated.
|
||||
|
||||
**Fase B (questa slice):** gerarchia component→pin→net→block; derating PASS/MARGIN/RISK da Vop/Vrated; timing RC/strap solo con numeri; PI local vs bulk; ESD e return path come REVIEW.
|
||||
|
||||
**Fase B close-out:** BOM↔PCB↔datasheet (package/pinout/V/T/I); SPOF REVIEW; EMI solo con FACT in `layout_rules`; Tj da θJA+rame+via quando tutti i parametri ci sono.
|
||||
|
||||
**Fase B leftovers:** thermal FEM / OpenEMS; ImpedenceFinder LICENSE UNKNOWN; auto-place pcbnew; GitHub fork detach.
|
||||
|
||||
**Fase A identity (shipped on product HEAD):** operator TOS/privacy/fence; not a `validate.py` rewrite.
|
||||
@@ -1,562 +0,0 @@
|
||||
# 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.** 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).
|
||||
|
||||
---
|
||||
|
||||
## 0b. Roadmap DeepSeek / crescita (integrata)
|
||||
|
||||
Fonte originale: canvas *Periscope: crescita e DeepSeek*. Qui lo stato operativo.
|
||||
|
||||
| Fase | Voce | Stato |
|
||||
| --- | --- | --- |
|
||||
| P0 | `deepseek-flash`, vision, pricing USD, effort | **Done** |
|
||||
| P0-5 | Smoke `simple_project` | **Done** offline — `scripts/smoke_simple_project.py` (+ `--live`) |
|
||||
| P1 | PDF drop log + cache hit-rate/stage | **Done** (parziale: cap ancora 500k; no off-peak/web_search) |
|
||||
| P1 | Thinking vs tools | Partial — review auto fino ultimo turno |
|
||||
| P2 | Eval, fingerprint, KiCad GA, PCB nets | **Done** |
|
||||
| P2 | `shortest_path` tool + library write gate | **Done** |
|
||||
| P2 | Crystal CL + NC pin | **Done** — check deterministici (numeri solo se presenti) |
|
||||
| P3 | Plugin CI / chat report | Todo |
|
||||
| Layout F1 | Domini / gruppi / satelliti | **Done** — `functional_groups.json` (no mm) |
|
||||
| Layout F1b | Pipeline Placement parallela | **Done** — API/UI `placement_*`, `placement_plan.json` |
|
||||
| Layout F2 | Placement IC packing mm | **Partial** — skeleton gated (`placement_pack.json`); no zone/export yet |
|
||||
| C4 | `layout_rules` dal datasheet | **Improved** — skill 1.10.0 + trim keywords + cache refresh se vuoto |
|
||||
|
||||
**Done when (prossimo pacchetto):** smoke `--live` verde; hit_ratio visibile in UI logs; F2 packing oltre skeleton (collisioni, zone, export); più IC library con `layout_rules` numerici.
|
||||
|
||||
---
|
||||
|
||||
## 0c. Placement / floorplan (routing-first)
|
||||
|
||||
Obiettivo unico: **routing migliore** (loop corti, meno crossing, canali liberi) — non bellezza dei footprint.
|
||||
|
||||
| Step | Cosa | Artefatto / stato |
|
||||
| --- | --- | --- |
|
||||
| 1 | Chip chiave (`ComponentType.IC`, rank per subtype) | F1 |
|
||||
| 2 | Domini = isole sulle **power nets** (non geometria) | F1 |
|
||||
| 3 | Gruppi minori: decoupling, bulk, load_cap, filter, pullup, … | F1 `role_hint` |
|
||||
| 4 | `layout_rules` già estratti sull’IC (nessun mm inventato) | F1 attach |
|
||||
| 5 | `assemble_order` dominio → chip → gruppi (contratto packer) | F1 metadato |
|
||||
| 6 | Packing mm / zone PCB / export | **F2 partial** — `placement_pack.json` (skip senza PCB + `max_distance_mm`); zone/export dopo |
|
||||
|
||||
### Roadmap Placement (da qui a coordinate utili)
|
||||
|
||||
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 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` (PE-PLC*) — non confondere con packing.
|
||||
|
||||
---
|
||||
|
||||
## 0. Due prodotti (stesso repo, due promesse)
|
||||
|
||||
| | **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 (`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 `PE-PLC` se il PCB non c’è.
|
||||
|
||||
Non serve un fork oggi. Serve disciplina: ogni PR dichiara se è Core o Layout.
|
||||
|
||||
---
|
||||
|
||||
## 0. Contratto di prodotto (non negoziabile)
|
||||
|
||||
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.
|
||||
|
||||
### Ingresso ufficiale: progetto KiCad (non EasyEDA)
|
||||
|
||||
Coppia da chiedere all’utente (e da salvare insieme):
|
||||
|
||||
| File | Ruolo |
|
||||
| --- | --- |
|
||||
| **Root `.kicad_sch`** (+ fogli figli) | Connettività, ref, uuid simbolo/pin, MPN nei property |
|
||||
| **`.kicad_pcb`** | Rame, net, coppie, courtyard, stackup |
|
||||
|
||||
BOM CSV resta utile se i property MPN nello schema sono vuoti; se lo schema è completo, la BOM è opzionale.
|
||||
|
||||
**EasyEDA è fuori scope.** Niente plugin, niente parser JSON EasyEDA, niente DRC sull’editor cloud. Chi arriva da EasyEDA resta sul percorso già documentato (export PADS), senza lavoro nuovo.
|
||||
|
||||
**Gerber** non è l’ingresso primario: con `.kicad_pcb` i net ci sono già. I Gerber restano un eventuale piano B, non lo sprint 1 del layout.
|
||||
|
||||
### Schema gerarchico (più file) — attenzione
|
||||
|
||||
Oggi `parse_kicad_sch` legge **un solo foglio**. I `(sheet …)` che puntano ad altri `.kicad_sch` **non vengono seguiti**. L’errore attuale chiede di esportare la netlist.
|
||||
|
||||
Da fare (Wave A1), in ordine:
|
||||
|
||||
1. Upload: cartella progetto o zip, **oppure** il `.kicad_pro` + root schematic. Non un solo foglio figlio.
|
||||
2. Dal root: camminare ogni `(sheet (property "Sheetfile" "power.kicad_sch") …)` (KiCad 6–10: `Sheetfile` / path relativo al foglio padre).
|
||||
3. Path traversal: solo file sotto la root del progetto; rifiutare `../`.
|
||||
4. Unire la connettività:
|
||||
- **local label** restano nel foglio;
|
||||
- **hierarchical_label** sul figlio ↔ **sheet pin** sul padre (stesso nome);
|
||||
- **global_label** e power symbol (`#PWR`) sono globali su tutto il progetto.
|
||||
5. Path gerarchico KiCad (`/power/U1` vs `U1`): normalizzare i **Reference** come li vede il PCB (di solito già unici; se duplicati, è un errore dello schema).
|
||||
6. Uuid: `(uuid …)` su symbol e pin, più `sheet` uuid, per il plugin (pan-and-zoom sul foglio giusto).
|
||||
7. Fixture di test: root + 2 figli (alimentazione + analog), label gerarchiche su un net, power flag GND condiviso. Deve coincidere col netlist XML esportato da KiCad 9.
|
||||
|
||||
**Done when:** progetto a 3 fogli senza export netlist produce gli stessi `(ref, pin, net)` del file `*.xml` di KiCad.
|
||||
|
||||
`.kicad_pcb` è un file solo (il board non è gerarchico come lo schema). I net name nel PCB devono matchare i net risolti dallo schema dopo il flatten gerarchico.
|
||||
|
||||
Regole:
|
||||
|
||||
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.
|
||||
5. Plugin CAD **consumano** il report; non duplicano la pipeline.
|
||||
|
||||
### Estensione schema finding (fare per prima, una volta)
|
||||
|
||||
Oggi: `designator`, `mpn`, `aspect`, `finding`, `why`, `status`, `source_page`, `recommendation`, `source`.
|
||||
|
||||
Aggiungere (backward compatible):
|
||||
|
||||
| Campo | Serve a |
|
||||
| --- | --- |
|
||||
| `net` | Telemetry CAD, filtri, SI |
|
||||
| `pins[]` | Pan-and-zoom su U1.4 |
|
||||
| `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/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.
|
||||
|
||||
**Done when:** un finding di decoupling ha `rule_id` + `pins` e il report non rompe i finding LLM vecchi.
|
||||
|
||||
---
|
||||
|
||||
## Mappa lista ↔ codice attuale
|
||||
|
||||
| Blocco utente | Già c’è | Buco | Stato |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 Plugin / telemetry / BOM match | Parser KiCad XML/sexp/`.kicad_sch` gerarchico; wizard BOM; `plugins/kicad/` + cad-bridge JSON | EasyEDA fuori scope; uuid/sheet sul plugin da verificare su board reale | **OK** |
|
||||
| 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 (`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; `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** |
|
||||
|
||||
---
|
||||
|
||||
## Extra obbligatori (non nella lista, ma bloccanti)
|
||||
|
||||
Senza questi i moduli 1–12 non si misurano e i plugin mentono.
|
||||
|
||||
**E0. Eval harness.** Golden findings su `simple_project/` + 1–2 board interni. Precision/recall, % Unverified, citation hit-rate (`quote_verify`). Ogni check nuovo deve aggiungere fixture.
|
||||
|
||||
**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** (`periscope-cad-bridge` JSON). Un file per progetto:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"project_id": "...",
|
||||
"findings": [
|
||||
{
|
||||
"rule_id": "PE-MUX-001",
|
||||
"ref": "U3",
|
||||
"pins": ["12"],
|
||||
"sheet": "...",
|
||||
"uuid": "...",
|
||||
"severity": "error",
|
||||
"message": "...",
|
||||
"url": "https://periscope.../report?finding=U3-001"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Un solo adapter: **KiCad 9/10**. Niente secondo plugin EasyEDA.
|
||||
|
||||
|
||||
**E3. Smoke DeepSeek V4.1** su `simple_project` (vision + finding count vs run precedente). Già in coda P0.
|
||||
|
||||
---
|
||||
|
||||
## Wave A — Fondamenta CAD e finding (sblocca 1 e 6)
|
||||
|
||||
Obiettivo: schema KiCad fidato, finding indirizzabili, sync unidirezionale.
|
||||
|
||||
### A1. KiCad nativo “GA” (schema gerarchico)
|
||||
|
||||
Passi: quelli in “Schema gerarchico (più file)” + file-guide riscritta (niente “esporta PADS da KiCad” come percorso principale) + `cad_index.json` (ref → uuid → sheetfile).
|
||||
|
||||
**Done when:** fixture a più fogli = netlist XML KiCad; upload zip/progetto senza chiedere l’export.
|
||||
|
||||
### A3. Bridge KiCad (punto 1)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Pacchetto `plugins/kicad/` (action plugin Python, KiCad 9/10).
|
||||
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.
|
||||
|
||||
**Done when:** clic su finding centra U3 sul foglio `power.kicad_sch`, non sul root vuoto.
|
||||
|
||||
EasyEDA: **non si fa.**
|
||||
|
||||
### A2. BOM-to-schematic matching (punto 1)
|
||||
|
||||
Non è uno script a parte: è il grafo.
|
||||
|
||||
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=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.
|
||||
|
||||
---
|
||||
|
||||
## Wave B — Check deterministici schema (sblocca 4, 5, 6, 9, 10 DNP)
|
||||
|
||||
Obiettivo: meno LLM, più numeri. Ogni item = modulo `periscopex` + test su grafo sintetico + riga in eval.
|
||||
|
||||
### B1. Power tree & drop (6)
|
||||
|
||||
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 `PE-PWR-001` margin fail.
|
||||
|
||||
### B2. Sequencing (6)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Estrarre da datasheet (skill specs o tabella) `power_sequence` se presente; altrimenti skip.
|
||||
2. Sul grafo: enable pin, RC delay, PG (power good) concatenati.
|
||||
3. WARNING se sequenza dichiarata e PG non collega l’enable del rail successivo; niente invenzione di millisecondi.
|
||||
|
||||
### B3. Pull-up dimensionamento (6)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Estendere `check_i2c_pullups`: presenza **e** valore vs Vrail e Cf (formula NXP UM10204, bound largo).
|
||||
2. Reset: stesso, più pull-down vietato se datasheet dice active-low con pull-up interno assente.
|
||||
|
||||
### B4. Mux (6)
|
||||
|
||||
Già `pin_mux_check.py`. Passi residui: coprire SPI/UART con gli stessi token; test su MSPM0 del `simple_project`.
|
||||
|
||||
### B5. Decoupling audit (5)
|
||||
|
||||
Già `check_supply_decoupling`. Passi:
|
||||
|
||||
1. Distinguere Cin LDO vs Cout (pin IN/OUT dal pintable, non solo VDD).
|
||||
2. Contare valore se specs passive ci sono (100 nF vs 10 µF) come WARNING non ERROR se il vendor non è esplicito.
|
||||
|
||||
### B6. Derating DC bias (5)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Tabella empirica C0G/X7R/X5R (loss % vs V/Vrated) in `derating.py` — etichettare “stima”, non misura.
|
||||
2. UI derating: colonna C_eff.
|
||||
3. Non spacciare per curva Murata del lotto.
|
||||
|
||||
### B7. ESR/ESL parallelo (5)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Modello grezzo: C + ESL package (0402/0603 lookup) + ESR da specs se c’è.
|
||||
2. Z(f) somma parallelo 10µF+100nF; confronta con f_sw buck se nota.
|
||||
3. Senza f_sw: INFO “copertura HF dipende da 100nF vicino al pin” senza fingere un target Ω.
|
||||
|
||||
### B8. Filtri (4)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Matcher topologico sul grafo: RC serie-shunt, LC, ferrite+C, π (C-L-C), T (L-C-L).
|
||||
2. \(f_c\) per RC (`1/2πRC`) e LC (`1/2π√LC`); Pi/T solo se L e C noti.
|
||||
3. Confronto con `adc_sample_rate` / `data_rate` **solo se** in specs IC.
|
||||
4. Insertion loss: WARNING se ferrite DCR alta su rail ADC analog (soglia da datasheet o skip).
|
||||
|
||||
### B9. Termico schema (9)
|
||||
|
||||
Passi:
|
||||
|
||||
1. \(P \approx I_{load} \times (Vin-Vout)\) per LDO se I e V noti; \(T_j \approx T_a + P \theta_{JA}\) con \(T_a=25\) default e campo UI.
|
||||
2. Resistori: \(P=I^2R\) se I dal LED check o da shunt + V; vs `power_rating_w`.
|
||||
3. Senza θJA: non inventare; INFO “manca theta_ja”.
|
||||
|
||||
### B10. DNP / varianti (10)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Campo BOM `DNP` / `fitted` / `variant`.
|
||||
2. Grafo per variante: pin Enable senza pull e senza driver → ERROR.
|
||||
3. Non è layout.
|
||||
|
||||
---
|
||||
|
||||
## Wave C — Datasheet intelligence (punto 2)
|
||||
|
||||
Restare su DeepSeek Flash vision; niente secondo vendor di default.
|
||||
|
||||
### C1. Parsing adattivo (già in parte)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Eval pintable su 10 PDF: TI, STM, NXP, Espressif, Winbond, GigaDevice, Holtek, Silergy, 3 PEAK.
|
||||
2. Dove fallisce: skill `extract-pintable` + pagine vision (già keyword). Non RAG vettoriale finché l’eval non lo chiede.
|
||||
3. Se serve RAG: chunk per pagina in libreria, retrieval per pin/abs-max — **dopo** C1 eval.
|
||||
|
||||
### C2. Errata
|
||||
|
||||
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 `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.
|
||||
|
||||
### C3. OCR / vision block diagram (2)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Non un modello CV a parte: riusare pagine “block diagram” già in `_PAGE_KEYWORDS`.
|
||||
2. Tool extraction: `internal_features: {esd_clamp_pins[], pullup_pins[], analog_switch[]}`.
|
||||
3. Check: pin dichiarato open-drain senza pull visibile.
|
||||
|
||||
### C4. Layout rules dal datasheet (placement proposto)
|
||||
|
||||
La pagina “Typical application / PCB layout” non è solo uno schema: spesso dice *quanto vicino*, *quanti via*, *da che lato del package*. Va estratto in **struttura**, non lasciato in prosa al reviewer.
|
||||
|
||||
Skill o estensione specs (libreria per MPN, versionata come il pintable):
|
||||
|
||||
```json
|
||||
{
|
||||
"layout_rules": [
|
||||
{
|
||||
"kind": "decoupling_proximity",
|
||||
"pin": "VDD",
|
||||
"cap_value_hint": "100nF",
|
||||
"max_distance_mm": 2.0,
|
||||
"same_layer": true,
|
||||
"source_page": 14
|
||||
},
|
||||
{
|
||||
"kind": "thermal_via",
|
||||
"pin": "EP",
|
||||
"min_via_count": 4
|
||||
},
|
||||
{
|
||||
"kind": "keepout",
|
||||
"net_class": "analog",
|
||||
"note": "no digital return under analog pin"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Passi:
|
||||
|
||||
1. Pagine già keyword-matched (`application`, `layout`, `decoupling`, `PCB`). Vision obbligatoria (disegni).
|
||||
2. `validate.py` sulla lista: `kind` enum chiuso; distanze solo se il testo/figura ha un numero; altrimenti `max_distance_mm` null e il check G2 usa una default **dichiarata** (es. 3 mm) con severity WARNING.
|
||||
3. Citazione `source_page` + quote_verify sul testo se c’è (“place within 2 mm”).
|
||||
4. Non inventare una land pattern JEDEC se il PDF non la dà.
|
||||
|
||||
**Done when:** MSPM0 o LDO del `simple_project` ha almeno una `decoupling_proximity` in library JSON, o skip esplicito `layout_rules: []` con log.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Wave D — Impedenza analitica (punto 3) *senza* PCB
|
||||
|
||||
Motore **standalone**, stile calcolatrice. I vincoli CAD sono export, non verità sul board.
|
||||
|
||||
### D1. Motore Wheeler/Schneider
|
||||
|
||||
Passi:
|
||||
|
||||
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).
|
||||
|
||||
### D2. Stackup → regole CAD
|
||||
|
||||
Passi:
|
||||
|
||||
1. Form stackup (N layer, h, er).
|
||||
2. Output: w/s per 50 / 90 USB / 100 diff.
|
||||
3. Export KiCad `.kicad_dru` o netclass — **consigli**, l’utente applica.
|
||||
4. Nessun finding “traccia troppo stretta” finché non c’è `.kicad_pcb`.
|
||||
|
||||
---
|
||||
|
||||
## Wave E — RF schema (punto 7, parte schema)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Topologia π/T tra pin ANT e connettore/antenna (stesso matcher filtri). **OK** — sezione Verify in RF/Impedance.
|
||||
2. Target 50 Ω come **intento**, non misura: WARNING se manca rete. **OK** (status warning su `missing`).
|
||||
3. Utility Progetta: marker KiCad `ANT*` / `ANT_FEED` + stackup → `w` microstrip; `f0` → template IFA / meander / stub (segmenti mm + SVG + `.kicad_mod`); zona `antenna` → fit/scale. **OK** (auto-place rame nel PCB = dopo).
|
||||
4. CPW clearance: **Wave G** (layout).
|
||||
|
||||
---
|
||||
|
||||
## Wave F — Supply chain (punto 11)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Estendere DigiKey/Mouser/LCSC: campi `lifecycle`, `rohs`, `stock`, `lead_time` già spesso nel payload.
|
||||
2. Job periodico (non ogni review): `lifecycle.json` per MPN libreria.
|
||||
3. Finding INFO/WARNING EOL/NRND; RoHS fail solo se il flag è esplicito.
|
||||
4. Cross-ref: **solo** se il distributore dà `replacement` / family; niente LLM “equivalente”.
|
||||
|
||||
---
|
||||
|
||||
## Wave G — Layout (punti 3 residui, 7 CPW, 8, 10 SI)
|
||||
|
||||
Due file per progetto: **schema KiCad flatten** + **`.kicad_pcb`**.
|
||||
|
||||
**G0. Ingest layout**
|
||||
|
||||
Passi:
|
||||
|
||||
1. Parser `.kicad_pcb`: tracce, via, zone, layer, **net name**, footprint, courtyard, differential pairs se presenti.
|
||||
2. I net del PCB devono combaciare con quelli dello schema dopo il flatten gerarchico (stesso `U1.4` ↔ pad).
|
||||
3. Modello `LayoutGraph` affiancato a `DesignGraph`.
|
||||
4. Gerber: non in questa wave. Solo se un utente non può dare il `.kicad_pcb`.
|
||||
|
||||
**G1. Check (solo se net e geometria sono legati)**
|
||||
|
||||
Passi:
|
||||
|
||||
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.
|
||||
5. CPW: gap verso GND copper vs valore del calcolatore D2. — CPWG non nel vendor.
|
||||
|
||||
**G2. Placement vs datasheet (piste, decoupling, thermal)**
|
||||
|
||||
**G2. Placement vs datasheet (piste, decoupling, thermal)**
|
||||
|
||||
Lo schema dice se C12 è sul net VDD. Il PCB dice se C12 è a 8 mm dal pad. Questo check è **solo layout + layout_rules**.
|
||||
|
||||
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** (`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 `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) → `PE-PLC-001`; cap a 1 mm → niente finding.
|
||||
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
## Wave H — Enterprise (punto 12)
|
||||
|
||||
Passi:
|
||||
|
||||
1. Stato finding: `open | false_positive | accepted | wontfix` + motivo obbligatorio.
|
||||
2. Firma rilascio: hash report + user + timestamp (OSS: locale; cloud: già Clerk).
|
||||
3. ECO: export `eco.json` / CSV: rule_id, ref, before/after raccomandazione, finding_id.
|
||||
4. Dashboard: filtri già URL; aggiungere coda “da approvare”.
|
||||
|
||||
Commenti esistono: non rifarli, agganciarli allo stato.
|
||||
|
||||
---
|
||||
|
||||
## Ordine di esecuzione (sviluppo)
|
||||
|
||||
Non parallelizzare A0 schema finding con G.
|
||||
|
||||
| Sprint (indicativo) | Contenuto | Dipende da | Stato |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 | Schema finding + eval + smoke V4.1 | — | **OK** |
|
||||
| 1 | A1 KiCad GA + A2 BOM match | 0 | **OK** |
|
||||
| 2 | B3–B5 pull-up/decoupling/Cin-Cout | 0 | **OK** |
|
||||
| 3 | B6–B9 DC bias, ESR, filtri, termico | 2 | **OK** |
|
||||
| 4 | B1–B2 power drop/sequencing + B10 DNP | 1 | **OK** |
|
||||
| 5 | A3 plugin KiCad + E2 JSON | 1, 0 | **OK** |
|
||||
| 6 | C2 errata + C3 internal features + **C4 layout_rules** | eval C1 | **OK** |
|
||||
| 7 | D1–D2 calcolatrice impedenza + export netclass | — | **OK** |
|
||||
| 8 | F lifecycle | APIs già presenti | **OK** |
|
||||
| 9 | H review workflow / ECO | 0 | **OK** |
|
||||
| 10+ | G layout `.kicad_pcb` + G2 placement datasheet | A1 + C4 | **OK** (G1 3W/creepage/CPW/HV ancora skip senza numero) |
|
||||
|
||||
Stima onesta: Wave A–B (schema) sono il ritorno; G è un secondo prodotto. Non promettere creepage nel plugin schema.
|
||||
|
||||
---
|
||||
|
||||
## Passi operativi per **ogni** check nuovo
|
||||
|
||||
1. Fixture grafo minimo in `tests/test_<nome>.py` (non solo `simple_project`).
|
||||
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.
|
||||
6. Se tocca UI: tab o badge “Automated check” già usato.
|
||||
7. Browser solo se UI; senno pytest.
|
||||
|
||||
---
|
||||
|
||||
## Fuori scope (esplicito)
|
||||
|
||||
- Plugin o parser **EasyEDA**.
|
||||
- Agente che **progetta** lo sbroglio.
|
||||
- Simulazione SPICE/IBIS.
|
||||
- Sostituti pin-to-pin inventati dall’LLM.
|
||||
- Scraping errata senza whitelist.
|
||||
- Layout da netlist PADS dump (`!PADS-POWERPCB`) — già rifiutato.
|
||||
|
||||
---
|
||||
|
||||
## Prossimo passo concreto (quando si apre lo sviluppo)
|
||||
|
||||
Sprint 0–10+ del piano di lavoro sono **OK** nel codice (parametro o skip).
|
||||
|
||||
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 `PE-PLC`/`PE-SI` sul board.
|
||||
|
||||
Il plugin KiCad aspetta ancora verifica uuid + sheet su un progetto multi-foglio vero.
|
||||
@@ -1,137 +0,0 @@
|
||||
# Piano — PCB review (esame geometria + stesso motore finding)
|
||||
|
||||
Specifica finding: [`motore-finding.md`](motore-finding.md). Schema e PCB usano **lo stesso oggetto**. Questo file è il job layout.
|
||||
|
||||
**Non** è un auto-placer. PCB-only = geometria (mm, width, copper, via, pour, return stitch, pad-net). Validazione netlist / mux / LED resta schema.
|
||||
|
||||
Contratto:
|
||||
|
||||
| | Schema | PCB review | Placement F2 (fuori) |
|
||||
| --- | --- | --- | --- |
|
||||
| Promessa | Netlist vs libreria datasheet | Layout vs libreria + geometria | xy nuovi |
|
||||
| Motore finding | FACT / REQUIREMENT / INFERENCE | uguale | — |
|
||||
| Deep exam PDF | **Una volta** → `library/extracted` | Consuma cache, `pdf_path=None` | — |
|
||||
| Job | `MODE=run` | `MODE=pcb` | `MODE=placement` |
|
||||
| Output | `report.json` | `pcb_report.json` (merge in GET /report) | pack json |
|
||||
|
||||
Senza `.kicad_pcb` il progetto è schema-completo. Il report schema non si riempie di PE-PLC/PE-SI.
|
||||
|
||||
---
|
||||
|
||||
## Motore (schema + PCB)
|
||||
|
||||
Vedi `motore-finding.md`. Invarianti:
|
||||
|
||||
1. Non mescolare FACT / REQUIREMENT / INFERENCE.
|
||||
2. Provenance **MANDATORY | RECOMMENDED | TYPICAL | EXAMPLE** nel rule DB. **Recommended ≠ ERROR**.
|
||||
3. Classi **RULE | RISK | REVIEW | INFO**. LLM = REVIEW, mai RULE.
|
||||
4. `decisions.json` + fingerprint: rescan non ri-naga (PSEL low intenzionale, ecc.).
|
||||
5. `status`, `confidence`, `evidence_status` indipendenti.
|
||||
6. Evidenza insufficiente → lo si dice, niente 1 oz / 10 °C / 50 Ω / IEC inventati.
|
||||
7. Oggetto riproducibile: facts, requirement, source, calculation, assumptions, confidence, severity, action.
|
||||
|
||||
UI report (schema e layout): stessa `FindingCard` — Fact / Requirement / Inference, badge classe e provenance.
|
||||
|
||||
Libreria: `library/extracted/{safe_mpn}.json` ∪ extracted/ progetto. PDF in `library/datasheets/blobs/`.
|
||||
|
||||
---
|
||||
|
||||
## Job PCB (invariato nello stack)
|
||||
|
||||
```
|
||||
┌─ MODE=run → status → report.json
|
||||
Project files ───┼─ MODE=placement → placement_status → placement_plan.json
|
||||
└─ MODE=pcb → pcb_status → pcb_report.json
|
||||
```
|
||||
|
||||
Stages: `ensure_graph` → `parse_pcb` → `classify` → `inventory` → `checks` → `ai_review` (layout vs library, no PDF) → `write_report`.
|
||||
|
||||
Mutex vs analisi e vs placement. SSE `pcb_*`. IDs `PCB-{ref}-{001}`. KiCad `/net` vs net schema: **PE-LAY-003** una volta, non per-pad (`kicad_nets_match`).
|
||||
|
||||
### Check PCB (geometria) — gated
|
||||
|
||||
| ID | Gate |
|
||||
| --- | --- |
|
||||
| PE-LAY-001…003 | pad/ref/hierarchy nets |
|
||||
| PE-PLC-001…004 | `layout_rules` numerici |
|
||||
| PE-SI-001 | `length_match` mm on USB/HDMI/PCIe/ETH/LVDS/DDR nets (not I2C/GPIO/CC) |
|
||||
| PE-SI-002 | ImpedenceFinder Zavg/min/max vs `impedance` window |
|
||||
| PE-SI-003…009 | max length, spacing, ref plane, vias, return, series R — each a datasheet number |
|
||||
| PE-SI-010 | HS bus measured, library has no SI FACT (not 90 Ω folklore) |
|
||||
| PE-PWR-001 | I_load + width; IPC solo con thickness e Tjmax |
|
||||
| PE-DRT-001 | Vop e Vrated, Vop > Vr → RULE ERROR |
|
||||
| PE-DRT-002 | 0.8 < Vop/Vr ≤ 1 → RISK MARGIN |
|
||||
| PE-DRT-003 | Vop/Vr ≤ 0.8 → PASS summary INFO |
|
||||
| PE-HIER-001 | pin IC senza net |
|
||||
| PE-TIM-001/002 | RC / strap solo con numeri |
|
||||
| PE-PI-001/002 | local vs bulk; mm solo da layout_rules |
|
||||
| PE-ESD-001 / PE-RET-001 | REVIEW unless mandatory FACT |
|
||||
| PE-VIA-001 | via count + I_load (INFO, no tabella inventata) |
|
||||
| PE-THM-001 | P=I_load×drop, courtyard senza pour/via |
|
||||
| PE-THM-002 | Tj=Ta+P·θJA solo con P, θJA, area rame, conteggio via |
|
||||
| PE-BOM-010…014 | package / pinout / V / T / I quando BOM, PCB e datasheet hanno i numeri |
|
||||
| PE-SPOF-001 | un regolatore (o un XTAL condiviso) — REVIEW |
|
||||
| PE-EMI-001 | filtro EMI solo se `layout_rules` cita choke/ferrite/shield |
|
||||
| PE-KEL-001 | sense/Kelvin pintable + ≥2 altri sul net |
|
||||
| PE-STCH-001 | pour GND + segnale senza via GND nel bbox |
|
||||
| Creepage | **skip** senza numero datasheet/IEC |
|
||||
| Crystal keepout | PE-PLC-004 se kind=keepout |
|
||||
|
||||
AI PCB: skip IC senza pintable (`run schematic review first`).
|
||||
|
||||
---
|
||||
|
||||
## Fasi
|
||||
|
||||
**A — shipped:** oggetto finding condiviso, clamp provenance/class (**ERROR solo RULE+MANDATORY**), decisions, UI, libreria unica, geometria gated, net-slash, Action on card, geometria in contesto AI.
|
||||
|
||||
**B — questa slice (shipped, gated):**
|
||||
|
||||
| ID | Cosa | Gate |
|
||||
| --- | --- | --- |
|
||||
| Gerarchia | `hierarchy.json` component→pin→net→block | grafo (+ domains se `classify`) |
|
||||
| PE-HIER-001 | pin IC senza net | FACT net='' |
|
||||
| PE-DRT-001/002/003 | RISK / MARGIN / PASS | Vop **e** Vrated; niente % dielettrico inventato |
|
||||
| PE-TIM-001/002 | RC reset / strap | R, C e t_reset o Vih/Vil numerici |
|
||||
| PE-PI-001/002 | local vs bulk, I se noto | cap valorizzati; distanza solo con `max_distance_mm` |
|
||||
| PE-ESD-001 | ESD connettore/USB | REVIEW se manca parte ESD |
|
||||
| PE-RET-001 | return HS senza via GND | REVIEW, non ERROR |
|
||||
|
||||
**B — SI (shipped, gated):** ImpedenceFinder is **in the checks**, not only the impedance tab dump. Each datasheet SI requirement is one finding: PASS / FAIL / MARGIN + FACT / REQUIREMENT / source. A rule applies only to the **quoted bus**. **Skip** I2C, GPIO, EN, analog REGN, USB CC, strap/EN RC (not USB/DDR/PHY pairs). No invented 50/90 Ω.
|
||||
|
||||
### Coverage matrix (bus × requirement)
|
||||
|
||||
Gate: library `layout_rules` kind + `net_class`/quote on that bus. Empty cell = PE-SI-010 INFO (measurement only) if the bus is on the board; never a folklore 90 Ω ERROR.
|
||||
|
||||
| Bus | Z (002) | skew (001) | max L (003) | spacing (004) | ref/layer (005) | vias (006) | return (008) | series R (009) | no-Z (010) |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| USB2 D+/D− | gated | gated | gated | gated | gated | gated | gated | gated; **not** EN RC | measurement-only if no Z FACT |
|
||||
| USB3 SuperSpeed | gated | gated | gated | gated | gated | gated | gated | gated | per-bus |
|
||||
| ETH MDI / PHY+RJ45 | gated | gated | gated | gated | gated | gated | gated | gated | per-bus |
|
||||
| RGMII MAC–PHY | gated | gated | gated | gated | gated | gated | gated | gated | per-bus |
|
||||
| SGMII | gated | gated | gated | gated | gated | gated | gated | gated | per-bus |
|
||||
| DDR3 CLK/DQS/DQ/ADDR | gated | gated | gated | gated | gated | gated | gated | gated | per-subclass if no Z |
|
||||
| HDMI / PCIe / LVDS | gated | gated | gated | gated | gated | gated | gated | gated | per-bus |
|
||||
| I2C / GPIO / EN / CC | skip | skip | skip | skip | skip | skip | skip | skip | skip |
|
||||
|
||||
Emmaforo USB: extract with **no Z number** → Zavg ~88 Ω is **PE-SI-010** INFO (insufficient), **not** a 90 Ω FAIL. If library later has 90 Ω ±10% (81–99), Zavg ~88 Ω **PASS** on average and **MARGIN/FAIL** if min is outside the window; ~2.5 mm intra-pair skew **FAIL** only when `length_match` mm exists. PE-SI-009 must not fire on USB from ESP32 EN RC 10 kΩ / 1 µF p.28.
|
||||
|
||||
Pintable skill **1.13.0** (`net_class` required on SI kinds). Older extracts with ungated `series_resistor` refresh on schematic re-run (`si_extract_needed.json` on PCB).
|
||||
|
||||
**B — still missing (true leftovers):**
|
||||
|
||||
| Gap | Why it stays out |
|
||||
| --- | --- |
|
||||
| Thermal FEM / energy | PE-THM-002 is Tj=Ta+P·θJA with measured copper/vias; no spreading/FEM |
|
||||
| CPWG / OpenEMS | Field-solver export excluded from the ImpedenceFinder vendor snapshot |
|
||||
| ImpedenceFinder license | Upstream has **no LICENSE** (UNKNOWN). Not invented in-tree. See `vendor/impedancefinder/SOURCE.md` |
|
||||
| Auto-place / pcbnew write-back | Fase C |
|
||||
| Fork detach | GitHub parent Faradworks/Pinscope stays; identity Fase A does not unfork |
|
||||
|
||||
Shipped PCB close-out (2.35.0): BOM↔PCB↔datasheet (`PE-BOM-010`…`014`); SPOF REVIEW; EMI with datasheet FACT; gated θJA Tj; SI re-extract when library `layout_rules` lack SI kinds (`model_version` 1.12.0, `si_extract_needed.json`). Shared `library/extracted` + one PDF exam.
|
||||
|
||||
Shipped identity Fase A (2.36.0): operator Michele Bigi; TOS/privacy/contact/metadata; PinScope identifier fence. `validate.py` untouched.
|
||||
|
||||
**C — fuori:** auto-place, write-back pcbnew, unire i job, sshd/keys, fork detach / PinScope engine rewrite.
|
||||
|
||||
Deploy: `ssh periscope` + `/root/periscope/scripts/update-periscope.sh`.
|
||||
Reference in New Issue
Block a user