# 20260923 - Audit e riduzione uso memoria MCP Sophia

**File:** `20260923_audit_memory_usage.md`  
**Data:** 2026-09-23  
**Repository target:** `sophiadeveloper/mcp-servers`  
**Snapshot analizzato:** commit `c6f09d9c253a12b710c4a61bd0dc519585dc98e3` (`master` al momento dell'analisi)  
**Tipo documento:** piano implementativo per agenti  
**Priorità:** alta  
**Stato:** pronto per implementazione incrementale

---

## 1. Obiettivo

Ridurre in modo strutturale il consumo di RAM e il numero di processi MCP locali avviati automaticamente dai client AI/IDE, mantenendo compatibilità con il framework Sophia e senza ridurre in modo silenzioso le capability disponibili agli sviluppatori.

Il problema da correggere non è l'avvio automatico di Chromium da parte di Playwright: nel codice attuale `playwright-node` inizializza browser/context/page in modo lazy tramite `ensureBrowser()` / `ensureCdpBrowser()`. Il problema principale è che `playwright-node`, come gli altri server locali Sophia basati su `StdioServerTransport`, viene comunque avviato come processo Node quando il client inizializza gli MCP configurati.

Con più sessioni/worker/client, il modello `stdio` porta naturalmente a più processi indipendenti:

```text
client/sessione x MCP stdio abilitati = processi locali concorrenti
```

Nello scenario già osservato sono comparsi circa 60 processi MCP riconducibili a Codex, compatibili con circa 6 copie di un set di 10 MCP, e circa 2 GB di Private Working Set complessivo attribuibile ai runtime MCP nello scenario analizzato.

L'intervento deve quindi agire prima di tutto su:

1. quanti MCP vengono configurati/abilitati globalmente;
2. quali MCP devono essere globali, di progetto o manuali;
3. come installer e generatori traducono questa policy nei diversi client;
4. quanto pesa realmente ogni server a riposo e dopo l'uso;
5. corretto teardown dei processi e assenza di processi orfani;
6. ottimizzazioni locali di `playwright-node` solo dove misurabili.

---

## 2. Evidenze tecniche già verificate

### 2.1 `stdio` implica un processo per connessione/client

I server Sophia principali usano `StdioServerTransport`, inclusi almeno:

- `analytics-node`
- `cf-node`
- `git-node`
- `linter-node`
- `mantis-node`
- `memory-node`
- `office-node`
- `playwright-node`
- `projectfs-node`
- `sql-node`

Il modello MCP `stdio` prevede che il client avvii il server come child process. Un server `stdio` non è condiviso fra più client/sessioni.

Riferimenti:

- https://ts.sdk.modelcontextprotocol.io/client
- https://csharp.sdk.modelcontextprotocol.io/concepts/stateless/stateless.html

### 2.2 Playwright non avvia Chromium al boot del server

`playwright-node/index.js` parte con:

```js
launch: {
  browser: null,
  context: null,
  page: null
}
```

Il browser viene creato successivamente da `ensureBrowser()`:

```js
launchState.browser = await chromium.launch(launchOptions);
```

L'attach CDP è anch'esso demand-driven tramite `ensureCdpBrowser()`.

Quindi:

```text
avvio IDE -> processo node playwright-node
prima azione browser -> chromium.launch() / connectOverCDP()
```

### 2.3 Playwright importa però la libreria in modo eager

Attualmente il server contiene:

```js
import { chromium } from "playwright";
```

Anche senza Chromium attivo, ogni processo `playwright-node` carica quindi Node/V8, MCP SDK, codice server e package Playwright.

Questa non è la causa primaria della moltiplicazione, ma è una possibile ottimizzazione del footprint idle da misurare.

### 2.4 Il routing semantico e il lifecycle fisico non sono allineati

`scripts/runtime/state-manager.js` contiene `MCP_SERVER_REGISTRY` e marca i server con:

```js
suggestOnly: true
```

Per esempio `playwright-mcp-server` è già semanticamente trattato come capability da suggerire quando serve.

Tuttavia `suggestOnly` non impedisce al client di avviare il processo se il server è presente e abilitato nella configurazione MCP.

In pratica oggi è possibile avere:

```text
MCP non scelto dal routing
+
MCP comunque configurato globalmente
=
processo stdio residente inutilmente
```

### 2.5 L'installer possiede già selezioni per-MCP

`scripts/install-user-runtime.js` costruisce selezioni runtime-specifiche, ad esempio:

```text
codex_mcp_<server>
claude_mcp_<server>
cursor_mcp_<server>
copilot_mcp_<server>
```

La logica corrente `checked / unchecked / remove` e i merger esistenti devono essere estesi, non sostituiti con un secondo meccanismo parallelo.

### 2.6 Esiste già un manifest di availability

Il framework genera:

```text
~/.mcp-servers/mcp-availability.json
```

con stato di disponibilità dei server.

Questo file deve restare la fonte runtime per availability e deve essere versionato/esteso in modo backward-compatible se vengono aggiunte informazioni di activation/startup policy.

### 2.7 Codex supporta disabilitazione senza rimozione

La configurazione Codex corrente supporta:

```toml
[mcp_servers.<id>]
enabled = false
```

oltre a configurazione per-progetto tramite `.codex/config.toml` nei progetti trusted.

Riferimenti ufficiali:

- https://developers.openai.com/docs/config-file/config-reference
- https://developers.openai.com/docs/config-file/config-basic

Questo permette di mantenere una definizione globale di un MCP senza avviarne il processo e di abilitarlo a livello progetto quando opportuno.

### 2.8 Cleanup Playwright da correggere

In `playwright-node/index.js` il cleanup attuale è registrato anche su:

```js
process.on("exit", cleanup);
```

ma `cleanup` è asincrono e richiama `process.exit(0)`.

Il callback dell'evento Node `exit` non può completare lavoro asincrono affidabile e non deve essere usato per attendere `browser.close()`. Inoltre la stessa funzione è collegata a più eventi (`SIGINT`, `SIGTERM`, `exit`, `transport.onclose`), quindi va resa idempotente ed evitata la possibilità di re-entry / uscita ricorsiva.

Questa parte non spiega la moltiplicazione iniziale dei processi, ma può contribuire a teardown incompleto o browser child rimasti dopo chiusure anomale e va corretta nello stesso intervento.

---

## 3. Principi di implementazione obbligatori

1. **Non trasformare tutti i server in HTTP in questa iterazione.**
2. **Non introdurre un mega-MCP unico.**
3. **Non cambiare il contratto tool dei server.**
4. **Non rimuovere MCP dalle installazioni esistenti senza una migrazione esplicita o una scelta dell'utente.**
5. **Le nuove installazioni devono avere default più leggeri.**
6. **Le installazioni esistenti devono poter applicare esplicitamente il profilo ottimizzato con backup e rollback.**
7. **Non assumere hot-reload degli MCP nei client.** Se non verificato, documentare che il cambio richiede nuova sessione/restart del client.
8. **Usare il registry esistente come source of truth**, evitando liste duplicate di server/policy in più file.
9. **Misurare Private Working Set/RSS e process count**, non solo Working Set aggregato.
10. **Ogni modifica deve essere coperta da test automatici dove possibile e da una procedura ripetibile di benchmark.**

---

## 4. Fuori scope

Questa attività NON deve includere:

- analisi/subagent governance;
- problemi delle statistiche analytics su macOS;
- migrazione generale dei server a Streamable HTTP;
- redesign del routing semantico;
- refactor esteso dei tool Playwright;
- ottimizzazioni Windows generiche;
- tuning di Defender, Chrome, Edge, Roslyn o driver;
- un nuovo daemon/proxy MCP condiviso locale;
- un nuovo protocollo custom fra client e server.

Questi temi devono restare attività separate.

---

# 5. Piano di implementazione

## Fase 0 - Baseline riproducibile prima di cambiare comportamento

### Obiettivo

Creare una baseline automatizzata per evitare di ottimizzare sulla base di impressioni o Task Manager manuale.

### Nuovo script proposto

Creare:

```text
scripts/audit-mcp-memory.mjs
```

Lo script deve essere read-only e non deve terminare processi.

### Output richiesto

JSON machine-readable più tabella console.

Esempio logico:

```json
{
  "timestamp": "2026-09-23T...",
  "platform": "win32",
  "processes": [
    {
      "pid": 1234,
      "ppid": 1000,
      "server": "playwright-mcp-server",
      "runtime": "node",
      "command": ".../playwright-node/index.js",
      "rssMb": 62.1,
      "privateMb": 47.8,
      "parentChain": [1000, 900]
    }
  ],
  "summary": {
    "mcpProcessCount": 12,
    "privateMb": 530.4,
    "rssMb": 820.3
  }
}
```

### Requisiti piattaforma

Implementare adapter separati:

- Windows: PowerShell/CIM o API equivalente per PID, PPID, command line e memoria privata;
- macOS/Linux: `ps`/proc dove disponibile;
- nessuna dipendenza npm pesante solo per l'audit.

Se una metrica non è disponibile sulla piattaforma, restituire `null` e una capability flag, non inventare equivalenze.

### Identificazione server

Associare il processo al server Sophia usando path/entrypoint del `MCP_SERVER_REGISTRY` e `SERVER_FOLDERS`, evitando regex hardcoded duplicate per ogni server.

### Scenari baseline da registrare

1. sistema senza client AI aperto;
2. IDE aperto, nessuna sessione attiva;
3. 1 sessione;
4. 2 sessioni;
5. 3 sessioni;
6. 6 sessioni;
7. chiusura 6 -> 3 -> 1 -> 0;
8. `playwright-node` senza chiamate browser;
9. `playwright-node` dopo una chiamata browser;
10. `playwright-node` dopo idle timeout.

### Deliverable

Salvare i report manuali di benchmark sotto:

```text
docs/audit/mcp-memory-usage-2026-09/
```

Non committare dump contenenti path utente sensibili senza sanitizzazione.

### Acceptance criteria Fase 0

- lo script identifica correttamente tutti i processi Sophia noti;
- distingue PID e PPID;
- produce almeno RSS e, su Windows, Private Working Set/Private Bytes coerente con la metrica scelta e documentata;
- non modifica configurazioni né processi;
- stesso output schema su Windows/macOS/Linux anche se alcune metriche sono `null`;
- documentato il significato esatto di ogni metrica.

---

## Fase 1 - Introdurre una startup policy canonica nel registry

### File principale

```text
scripts/runtime/state-manager.js
```

### Modifica richiesta

Estendere ogni entry di `MCP_SERVER_REGISTRY` con un campo canonico, ad esempio:

```js
startupPolicy: 'always' | 'project' | 'manual'
```

Semantica:

| Valore | Significato |
|---|---|
| `always` | candidato all'abilitazione globale di default |
| `project` | deve essere attivato solo nei progetti che ne hanno bisogno |
| `manual` | capability specialistica/rara, non abilitata globalmente di default |

Non usare `suggestOnly` per questa funzione: `suggestOnly` resta routing semantico; `startupPolicy` governa il lifecycle/configurazione.

### Classificazione iniziale obbligatoria

Applicare subito solo le classificazioni a basso rischio:

| MCP | startupPolicy iniziale | Motivazione |
|---|---|---|
| `playwright-mcp-server` | `manual` | specialistico, browser host-managed preferito quando disponibile |
| `office-mcp-server` | `manual` | specialistico e non necessario nella maggioranza delle sessioni |
| `analytics-mcp-server` | `manual` | diagnostica/analytics, non requisito per sviluppo normale |
| `cf-mcp-server` | `project` | dipende dallo stack del progetto |
| `sql-mcp-server` | `project` | dipende dal progetto/DB/credenziali |
| `mantis-mcp-server` | `project` | dipende dal workflow/ticketing del progetto |
| `linter-mcp-server` | `project` | utile solo dove lo stack/config lo richiede |

Per i seguenti server NON cambiare il comportamento di default nella prima PR finché i test di dipendenza non dimostrano che non sono richiesti da hook/routing/core workflow:

- `git-mcp-server`
- `docs-mcp-server`
- `memory-mcp-server`
- `projectfs-mcp-server`

Questi possono inizialmente restare `always` per compatibilità e venire rivalutati dopo la baseline.

### Catalogo generato

Aggiornare:

```text
scripts/build-mcp-catalog.mjs
scripts/hooks/mcp-catalog-schema.mjs
scripts/hooks/mcp-catalog-runtime.mjs
scripts/test-routing-engine.mjs
```

per includere e validare `startupPolicy`.

### Backward compatibility

- se un catalogo vecchio non contiene `startupPolicy`, assumere `always`;
- non rompere i consumer esistenti che leggono solo `id`, `capabilities`, `riskClass`, `suggestOnly`, `availability`;
- aggiornare eventuale fingerprint/schema version in modo deterministico.

### Availability manifest

Non sostituire `availability` con nuovi stati incompatibili.

Se serve rappresentare la policy nel manifest persistente, aggiungere un campo separato:

```json
{
  "version": 2,
  "servers": [
    {
      "id": "playwright-mcp-server",
      "availability": "configured",
      "startupPolicy": "manual"
    }
  ]
}
```

Il manifest deve avere versione esplicita e comportamento definito in caso di file assente/corrotto.

### Acceptance criteria Fase 1

- registry unico e senza liste policy duplicate;
- `startupPolicy` validata dal catalog builder;
- catalog deterministico;
- manifest vecchio ancora leggibile;
- test routing e installer esistenti verdi.

---

## Fase 2 - Default di installazione ottimizzati senza rompere gli upgrade

### File principali

```text
scripts/install-user-runtime.js
scripts/runtime/config-merger.js
scripts/runtime/apply-plan.js
scripts/inject-user-mcp-settings.js
scripts/gui/app.js
scripts/test-user-runtime.js
genera_mcp_json.ps1
genera_mcp_json.sh
```

### Problema attuale

Il framework dispone di selezioni per-MCP, ma sui nuovi setup il comportamento tende a configurare l'intero catalogo. Questo rende `suggestOnly` inefficace dal punto di vista del consumo RAM.

### Nuovo concetto: profilo MCP

Aggiungere un profilo esplicito all'installer:

```text
lean
full
custom
```

Semantica:

- `lean`: abilita globalmente solo `startupPolicy=always`;
- `full`: preserva il comportamento legacy, tutti i server selezionati;
- `custom`: usa le selezioni esplicite dell'utente.

### Default

- **nuova installazione:** `lean`;
- **installazione esistente:** preservare lo stato corrente;
- non applicare automaticamente `lean` a utenti esistenti senza scelta esplicita.

### CLI

Aggiungere opzione, naming definitivo a scelta purché testato e documentato:

```text
--mcp-profile lean|full|custom
```

Aggiungere anche un'azione esplicita per migrare un'installazione esistente:

```text
--optimize-mcp-startup
```

Questa azione deve:

1. mostrare/dichiarare quali MCP globali verranno disabilitati/rimossi dal global scope;
2. creare backup tramite i meccanismi già esistenti;
3. non toccare configurazioni custom non Sophia;
4. salvare il nuovo stato installazione;
5. essere idempotente.

### GUI

Nella UI installer:

- mostrare il profilo corrente;
- mostrare per ogni MCP il badge `always / project / manual`;
- aggiungere azione chiara `Ottimizza avvio MCP`;
- evitare di cambiare silenziosamente checkbox già scelte da utenti esistenti.

### Importante

Non introdurre una seconda lista di MCP nel frontend. La GUI deve leggere i metadati derivati dal registry/runtime state.

### Acceptance criteria Fase 2

Nuova installazione `lean`:

- `playwright-mcp-server` non è attivo globalmente;
- `office-mcp-server` non è attivo globalmente;
- `analytics-mcp-server` non è attivo globalmente;
- `cf/sql/mantis/linter` non sono globali salvo selezione esplicita;
- i server `always` continuano a funzionare come prima.

Upgrade esistente:

- nessun server precedentemente selezionato viene rimosso senza `--optimize-mcp-startup` o scelta GUI equivalente;
- backup creato;
- rollback possibile reinstallando `full` o ripristinando backup.

---

## Fase 3 - Traduzione per-client della startup policy

Non tutti i client espongono gli stessi meccanismi. Implementare un adapter per runtime, non logica condizionale sparsa nei generatori.

### 3.1 Codex

Usare le capability native attuali.

Per MCP `manual` configurato globalmente ma non attivo:

```toml
[mcp_servers.playwright-mcp-server]
command = "...node..."
args = [".../playwright-node/index.js"]
enabled = false
```

Per progetto trusted, consentire override tramite:

```text
<project>/.codex/config.toml
```

con:

```toml
[mcp_servers.playwright-mcp-server]
enabled = true
```

Non assumere che una modifica al file venga applicata alla sessione già aperta. Testare il comportamento e, se necessario, richiedere nuova sessione/restart.

### 3.2 Claude Code

Evitare di mettere `project/manual` nel user scope globale.

Usare i meccanismi di scope del client disponibili nella versione supportata:

- user: solo capability realmente globali;
- local/project: capability specifiche del progetto.

Prima di implementare, il coding agent deve verificare la CLI/versione Claude Code supportata dal progetto e la documentazione corrente, perché sintassi e file di configurazione sono esterni al repository Sophia.

### 3.3 VS Code / Copilot

Per i server specialistici preferire configurazione workspace/progetto quando supportata dal client.

Non modificare indiscriminatamente `%APPDATA%/.../Code/User/mcp.json` / equivalente macOS/Linux per server `project/manual` nel profilo `lean`.

La logica di gestione profili VS Code già esistente deve continuare a funzionare.

### 3.4 Cursor

Applicare lo stesso principio:

- globali solo `always`;
- progetto per `project/manual` quando supportato;
- se il client/versione non offre un equivalente affidabile, non inventare un formato: lasciare il server non globale e documentare l'attivazione esplicita.

### 3.5 Antigravity / altri client

Usare la stessa matrice capability. Ogni adapter deve dichiarare almeno:

```text
supportsGlobalDisable
supportsProjectScope
supportsHotReload
supportsHttpMcp
```

Valori non verificati devono essere `unknown`, non `true` per supposizione.

### Nuovo modulo suggerito

Centralizzare le capability client in un file tipo:

```text
scripts/runtime/client-mcp-capabilities.js
```

Esempio struttura:

```js
{
  codex: {
    supportsGlobalDisable: true,
    supportsProjectScope: true,
    supportsHotReload: false // solo dopo verifica reale
  }
}
```

Se `supportsHotReload` non è stato testato, usare `null`/`unknown`.

### Acceptance criteria Fase 3

- nessun `manual` viene globalmente abilitato in un clean install `lean`;
- `project` può essere attivato per progetto almeno su Codex e Claude Code;
- gli adapter non sovrascrivono config custom dell'utente;
- dry-run mostra esattamente i cambi previsti;
- test unitari per ogni mapping runtime.

---

## Fase 4 - Ottimizzazione idle specifica di `playwright-node`

Questa fase deve essere implementata solo dopo avere una baseline della Fase 0, ma può essere sviluppata nella stessa milestone se i test dimostrano un beneficio misurabile.

### 4.1 Lazy import di Playwright

Sostituire l'import statico:

```js
import { chromium } from "playwright";
```

con un loader lazy centralizzato, ad esempio:

```js
let chromiumApi = null;

async function getChromium() {
  if (!chromiumApi) {
    const playwright = await import("playwright");
    chromiumApi = playwright.chromium;
  }
  return chromiumApi;
}
```

Poi in `ensureBrowser()` / `ensureCdpBrowser()`:

```js
const chromium = await getChromium();
```

### Vincoli

- una sola Promise/import concorrente: evitare doppio import in caso di chiamate parallele;
- preservare `chromium.launch()` e `connectOverCDP()`;
- non cambiare schema/tool/output;
- verificare startup e first-call latency;
- non mantenere la modifica se il risparmio è trascurabile o introduce regressioni.

Implementazione preferibile thread-safe a livello event-loop:

```js
let playwrightLoadPromise = null;

async function getChromium() {
  if (!playwrightLoadPromise) {
    playwrightLoadPromise = import("playwright").then(mod => mod.chromium);
  }
  return playwrightLoadPromise;
}
```

### Gate quantitativo

Confrontare almeno 10 avvii freschi del solo server prima/dopo.

Registrare:

- mediana Private Working Set/RSS idle;
- p95 startup handshake;
- latenza prima chiamata browser.

Tenere la modifica solo se:

- il footprint idle scende in modo chiaramente misurabile e ripetibile;
- nessuno smoke test regredisce;
- la prima chiamata browser resta entro una regressione accettabile documentata.

Non fissare un target MB arbitrario prima della misura.

---

## Fase 5 - Correzione shutdown e prevenzione processi/browser orfani

### Problema

Il cleanup Playwright corrente è asincrono ma viene collegato anche all'evento `exit`, dove Node non attende Promise/I/O asincroni.

Inoltre `cleanup()` chiama direttamente `process.exit(0)` ed è registrato da più sorgenti.

### Refactor richiesto

Introdurre un cleanup idempotente:

```js
let cleanupPromise = null;

function cleanupResources(reason) {
  if (cleanupPromise) return cleanupPromise;
  cleanupPromise = (async () => {
    // detach listener
    // clear timer
    // close browser launch
    // release CDP handle senza chiudere Chrome remoto
  })();
  return cleanupPromise;
}
```

Gestire separatamente uscita da segnale:

```js
async function shutdownFromSignal(signal) {
  try {
    await cleanupResources(signal);
  } finally {
    process.exit(0);
  }
}
```

### Eventi minimi

Gestire esplicitamente:

- `SIGINT`;
- `SIGTERM`;
- `transport.onclose`;
- chiusura stdin se rilevante per la versione SDK usata.

Valutare `SIGHUP` su macOS/Linux se supportato senza effetti collaterali.

### `process.on('exit')`

Non eseguire cleanup asincrono da `exit`.

Al massimo usare un handler sincrono per diagnostica, senza `process.exit()` ricorsivo.

### Error path

Valutare gestione controllata di:

- `uncaughtException`;
- `unhandledRejection`.

Se aggiunta, deve loggare su `stderr`, tentare cleanup best-effort e uscire con codice non-zero. Non nascondere errori fatali.

### Browser CDP

Il cleanup non deve chiudere il Chrome remoto collegato via CDP. Deve solo rilasciare listener/handle locali.

### Acceptance criteria Fase 5

- chiamare cleanup due volte non genera eccezioni;
- `transport.onclose` termina correttamente il server;
- SIGTERM chiude Chromium lanciato internamente;
- CDP remoto resta aperto;
- nessun handler asincrono viene affidato a `process.on('exit')`;
- dopo chiusura client, nessun `playwright-node` Sophia resta orfano nel test controllato;
- dopo uso `launch`, nessun processo Chromium Sophia resta orfano nel test controllato.

---

## Fase 6 - Test automatici e benchmark di concorrenza

### Estendere test esistenti

Aggiornare almeno:

```text
tests/smoke/playwright-node.smoke.mjs
scripts/test-user-runtime.js
scripts/test-routing-engine.mjs
scripts/check-user-runtime.js
```

### Nuovi test consigliati

```text
tests/runtime/mcp-startup-policy.test.mjs
tests/runtime/mcp-client-capabilities.test.mjs
tests/runtime/mcp-memory-audit.test.mjs
```

### Test A - Registry

Verificare:

- tutti i server hanno `startupPolicy` valida;
- nessun ID duplicato;
- `startupPolicy` non modifica tools/capabilities legacy;
- catalog builder fallisce su valore sconosciuto.

### Test B - Clean install lean

Su HOME temporanea:

- eseguire installer `lean`;
- verificare config generate;
- Playwright/Office/Analytics non globalmente attivi;
- CF/SQL/Mantis/Linter non globalmente attivi;
- config custom preesistenti preservate.

### Test C - Upgrade legacy

HOME temporanea con tutti gli MCP già configurati:

- eseguire upgrade normale;
- nessun server viene rimosso;
- eseguire `--optimize-mcp-startup`;
- solo i server previsti vengono disabilitati/rimossi dal global scope;
- backup esistente e ripristinabile.

### Test D - Codex override progetto

Costruire HOME/repo temporanei:

```text
~/.codex/config.toml
project/.codex/config.toml
```

Verificare rendering/merge atteso per MCP `manual`.

Non serve lanciare Codex nel test unitario: il comportamento del parser/generator è sufficiente. Il test end-to-end con client reale resta manuale.

### Test E - Playwright lazy

Avviare `node playwright-node/index.js`, completare handshake MCP/list tools senza tool browser e verificare che:

- non venga creato Chromium;
- se è stata implementata la lazy import, il package Playwright non venga caricato prima di un'azione che lo richiede, con un hook/test compatibile con ESM o metrica equivalente;
- `browser_get_capabilities` / status non causino launch.

### Test F - Shutdown

1. start server;
2. launch browser;
3. inviare SIGTERM / chiudere transport;
4. verificare exit code atteso;
5. verificare assenza processi browser figli dopo timeout ragionevole;
6. ripetere con CDP e verificare che il browser remoto resti vivo.

### Test G - Concorrenza reale 1/2/3/6

Da eseguire manualmente almeno su Codex e Claude Code:

| Sessioni | MCP processi Sophia | Private/RSS | Playwright processi | Orfani dopo chiusura |
|---:|---:|---:|---:|---:|
| 1 | | | | |
| 2 | | | | |
| 3 | | | | |
| 6 | | | | |
| 0 dopo teardown | 0 attesi | baseline | 0 attesi | 0 |

Eseguire sia con profilo `full` sia con `lean`.

### Interpretazione

Con `stdio` è normale che i server **abilitati** crescano con il numero di sessioni.

Il fix è considerato riuscito se:

```text
slope lean << slope full
```

perché il numero di MCP always-on è molto più piccolo.

Non dichiarare bug del client se:

```text
N sessioni -> N copie dello stesso stdio server
```

Dichiarare anomalia se:

- una singola sessione genera più copie non giustificate dello stesso server;
- i processi non spariscono alla chiusura della sessione;
- il numero continua a crescere a parità di sessioni;
- browser child rimangono dopo teardown.

---

# 6. Verifica dipendenze prima di declassare i server core

Dopo avere implementato le fasi precedenti, analizzare questi quattro MCP prima di passare da `always` a `project/manual`:

```text
git-mcp-server
docs-mcp-server
memory-mcp-server
projectfs-mcp-server
```

Per ciascuno verificare:

1. hook che lo presuppongono disponibile;
2. skill che lo dichiarano obbligatorio;
3. routing/hint automatici;
4. fallback disponibili;
5. frequenza reale di utilizzo nei dati analytics;
6. footprint idle misurato;
7. effetto UX quando non è disponibile.

Solo dopo questa review aggiornare `startupPolicy`.

Non declassare `memory-mcp-server` senza verificare le regole di memoria e i relativi hook.

---

# 7. Streamable HTTP: decisione rinviata ma preparata

La migrazione HTTP resta un possibile secondo intervento, non parte del fix immediato.

Dopo la riduzione del set always-on, usare i dati del benchmark per individuare server che soddisfano contemporaneamente:

- alto costo aggregato dovuto alla moltiplicazione;
- basso accoppiamento al filesystem locale;
- stato condivisibile o stateless;
- sicurezza/autenticazione gestibili centralmente;
- concorrenza multi-client supportabile.

Candidati da valutare successivamente, non da migrare ora:

- docs;
- analytics;
- mantis;
- altri servizi centralizzati.

Non considerare automaticamente HTTP per:

- filesystem locale;
- tool che devono lanciare processi nel workspace;
- credenziali strettamente user/session local;
- integrazioni che dipendono dal contesto macchina.

---

# 8. Ordine consigliato delle PR

## PR 1 - Measurement only

**Nessun cambio comportamento.**

Contenuto:

- `scripts/audit-mcp-memory.mjs`;
- test dello script;
- baseline Windows + almeno smoke macOS/Linux dell'adapter;
- report iniziale.

Gate: baseline disponibile e ripetibile.

## PR 2 - Startup policy model

Contenuto:

- `startupPolicy` nel registry;
- catalog/schema/runtime aggiornati;
- manifest versionato/backward-compatible;
- test catalog/routing.

Gate: zero cambi alle config generate esistenti.

## PR 3 - Lean install + migration path

Contenuto:

- profili `lean/full/custom`;
- default `lean` solo clean install;
- `--optimize-mcp-startup`;
- GUI;
- test upgrade/rollback.

Gate: clean install realmente non configura/abilita i server specialistici.

## PR 4 - Client scopes

Contenuto:

- adapter capability per client;
- Codex disabled/project override;
- Claude project/local scope;
- VS Code/Cursor/Antigravity solo dopo verifica capability;
- dry-run e test merge.

Gate: nessun overwrite di configurazioni custom.

## PR 5 - Playwright idle + shutdown

Contenuto:

- lazy import se benchmark favorevole;
- cleanup idempotente;
- rimozione cleanup asincrono da `exit`;
- smoke teardown/orphan.

Gate: browser behavior invariato e 0 orfani nei test.

## PR 6 - Final benchmark & documentation

Contenuto:

- benchmark 1/2/3/6 full vs lean;
- documentazione installer;
- troubleshooting;
- KPI before/after;
- decisione informata su eventuale fase HTTP successiva.

---

# 9. File impattati attesi

La lista seguente è indicativa. L'agente deve verificare il branch corrente prima di modificare.

| File | Tipo intervento |
|---|---|
| `scripts/runtime/state-manager.js` | registry + startup policy + manifest |
| `scripts/build-mcp-catalog.mjs` | propagazione/validazione policy |
| `scripts/hooks/mcp-catalog-schema.mjs` | schema |
| `scripts/hooks/mcp-catalog-runtime.mjs` | compatibility runtime |
| `scripts/install-user-runtime.js` | profili/default/migrazione |
| `scripts/runtime/config-merger.js` | merge config per stato/policy |
| `scripts/runtime/apply-plan.js` | applicazione selezioni |
| `scripts/inject-user-mcp-settings.js` | Codex/VS Code config |
| `scripts/gui/app.js` | UI profilo e badge policy |
| `genera_mcp_json.ps1` | output Windows coerente |
| `genera_mcp_json.sh` | output macOS/Linux coerente |
| `scripts/check-user-runtime.js` | diagnostica policy/config |
| `scripts/test-user-runtime.js` | regression installer |
| `scripts/test-routing-engine.mjs` | catalog/routing regression |
| `playwright-node/index.js` | lazy import + shutdown |
| `tests/smoke/playwright-node.smoke.mjs` | smoke lazy/shutdown |
| `scripts/audit-mcp-memory.mjs` | nuovo audit processi/RAM |
| `tests/runtime/*` | nuovi test policy/audit |
| `README.md` / docs tecniche | documentazione utente/operativa |

Evitare di modificare file generati manualmente se esiste già il relativo generator.

---

# 10. Comandi di validazione minimi

L'agente deve prima verificare gli script disponibili nel `package.json` corrente. Come minimo eseguire i check già presenti applicabili al branch:

```bash
node scripts/check-tool-schemas.js
node scripts/test-user-runtime.js
node scripts/test-routing-engine.mjs
node tests/smoke/playwright-node.smoke.mjs
```

Aggiungere i nuovi test runtime introdotti da questa attività.

Eseguire inoltre:

```bash
node scripts/check-user-runtime.js --json
node scripts/audit-mcp-memory.mjs --json
```

su ambiente di test.

Se il repository dispone di suite aggregate (`npm test`, `npm run test:*`, affected tests), eseguirle secondo le regole correnti del repository.

---

# 11. KPI e criteri di successo finali

## Funzionali

1. Nessuna regressione nei tool MCP esistenti.
2. Playwright non lancia Chromium finché non serve.
3. `manual/project` non sono globalmente attivi in clean install `lean`.
4. Utente esistente non perde capability senza opt-in alla migrazione.
5. Attivazione per progetto disponibile almeno per Codex e Claude Code.
6. Config custom non Sophia preservate.
7. Rollback documentato e testato.

## Processi

1. 0 processi Sophia orfani dopo chiusura completa dei client nel test controllato.
2. 0 Chromium lanciati da `playwright-node` rimasti dopo shutdown controllato.
3. CDP remoto non chiuso dal teardown del server Sophia.
4. Nessuna duplicazione ulteriore rispetto al numero di sessioni attive e server abilitati.

## Memoria

Misurare, non stimare.

Confrontare `full` vs `lean` a 1/3/6 sessioni e riportare:

- process count;
- Private Working Set/Private Bytes Windows;
- RSS macOS/Linux;
- memoria per server;
- memoria aggregata;
- browser child process memory separata dal server Node.

Il risultato deve dimostrare una riduzione significativa del footprint a parità di workflow. Il target numerico finale deve essere fissato solo dopo la baseline PR 1.

---

# 12. Rollout

### Step 1 - Developer test

Applicare `lean` solo a 1-2 sviluppatori pilota.

Verificare per alcuni giorni di uso reale:

- MCP mancanti in task comuni;
- necessità di riattivazioni frequenti;
- errori di routing dovuti ad availability;
- processi residui;
- RAM media.

### Step 2 - Nuove installazioni

Rendere `lean` default per clean install.

### Step 3 - Installazioni esistenti

Mostrare l'azione `Ottimizza avvio MCP`, senza forzare la migrazione.

### Step 4 - Rivalutazione core

Usare analytics + benchmark per decidere se `git/docs/memory/projectfs` possono essere ulteriormente declassati.

### Step 5 - Eventuale HTTP pilot

Solo dopo avere quantificato il residuo.

---

# 13. Rollback

Deve essere possibile tornare al comportamento legacy con una singola operazione supportata, ad esempio:

```text
--mcp-profile full
```

Il rollback deve:

- riabilitare/configurare tutti i server previsti dal catalogo;
- preservare configurazioni custom;
- non eliminare backup;
- aggiornare `installation-state.json` e `mcp-availability.json`;
- richiedere restart/new session solo dove necessario e documentarlo.

---

# 14. Vincoli per l'agente implementatore

1. Prima di modificare, leggere `AGENTS.md`, README e regole repository correnti.
2. Verificare che commit/file siano ancora allineati allo snapshot indicato in testa.
3. Non fare refactor non necessari.
4. Non duplicare il registry MCP.
5. Non introdurre nuove dipendenze npm per semplici funzioni OS se evitabili.
6. Non usare metriche RAM non equivalenti senza etichettarle correttamente.
7. Non dichiarare un memory leak del client senza prova di processi che restano dopo teardown o crescita non correlata alle sessioni.
8. Non considerare `N sessioni -> N processi stdio` un bug di per sé.
9. Non migrare MCP a HTTP in questa attività.
10. Non modificare la semantica dei tool Playwright.
11. Ogni cambio di configurazione utente deve supportare dry-run/backup secondo i pattern esistenti.
12. Aggiornare documentazione e test nella stessa PR del comportamento modificato.

---

# 15. Definition of Done

L'attività è chiusa quando tutte le condizioni seguenti sono vere:

- [ ] baseline before disponibile;
- [ ] script audit memoria/processi versionato e documentato;
- [ ] `startupPolicy` presente nel registry e nel catalogo;
- [ ] clean install `lean` implementata;
- [ ] legacy install preservata senza migrazione implicita;
- [ ] azione di ottimizzazione esplicita disponibile;
- [ ] policy per-client centralizzata;
- [ ] Codex usa `enabled`/project override in modo verificato;
- [ ] Claude Code evita user-scope per server specialistici nel profilo lean;
- [ ] VS Code/Cursor/Antigravity hanno comportamento verificato o fallback conservativo documentato;
- [ ] Playwright static import valutato con benchmark e reso lazy se vantaggioso;
- [ ] cleanup Playwright idempotente e senza async `exit` handler;
- [ ] test teardown senza processi/browser orfani;
- [ ] benchmark full vs lean 1/3/6 sessioni completato;
- [ ] tutti i test repository pertinenti verdi;
- [ ] README/documentazione aggiornati;
- [ ] rollback `full` verificato;
- [ ] report finale con KPI before/after prodotto.

---

# 16. Risultato architetturale atteso

Prima:

```text
IDE/sessione
  -> tutti gli MCP Sophia globali
       -> N processi Node stdio
       -> moltiplicazione per sessione
```

Dopo:

```text
IDE/sessione
  -> solo MCP realmente globali
  -> MCP di progetto solo nei progetti pertinenti
  -> MCP specialistici/manuali disabilitati finché non servono
       -> molti meno processi Node per sessione
```

Per Playwright:

```text
startup IDE
  -> nessun playwright-node nel profilo lean, salvo progetto/abilitazione esplicita

quando abilitato:
  -> playwright-node leggero in attesa
  -> package Playwright lazy se benchmark favorevole
  -> Chromium solo alla prima azione browser
  -> hibernation browser dopo inattività
  -> teardown deterministico alla chiusura
```

Questa soluzione riduce il problema alla radice senza introdurre subito nuova infrastruttura condivisa e mantiene aperta una successiva migrazione selettiva verso Streamable HTTP solo per i server che i dati dimostreranno realmente convenienti da centralizzare.
