Files
periscope/docs/development/PINSCOPE_INDEPENDENCE_PLAN.md
T
michele a63e5bd7ab Fase A identity on PCB HEAD: operator fence, keep BOM/SPOF/EMI.
Replace Faradworks Inc. TOS/privacy/contact/metadata with Michele Bigi.
Dual-read pinscope_* keys; write periscope_* only. AGPL LICENSE and
GitHub fork parent unchanged. validate.py not edited.
2026-09-20 12:41:17 +02:00

290 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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** lalbero 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 lalbero PinScope, togliere AGPL, o chiedere a Faradworks una licenza commerciale come se fossimo noi lupstream |
**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.
Laudit già dice: lindipendenza **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 lattribuzione “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 delloperatore Michele, con dipendenze dichiarate*, **non** “Periscope senza AGPL”. Lalbero 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”:** lintero 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 | — | AC; **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à) | SM | **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 è lunico 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 AB; **REWRITE** reviewer nativo | L | **C** |
| `models.Finding` vs `motore-finding.md` | **REWRITE** IR (compat shim) | ML | 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 | ML | **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 allaudit §11 punti 14: `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 56, 89.
- 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 AB)
Qui sì si riscrive lengine 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 lobiettivo 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 AD.
---
## 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; laudit 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)