# Analisi tecnica definitiva: `analytics-node` / `analytics-mcp-server`

Versione: 2026-05-28  
Stato: specifica tecnica MVP pronta per implementazione incrementale  
Repository target: `sophiadeveloper/mcp-servers`  
Vincolo runtime: Node.js / TypeScript, nessun Python

---

## 1. Obiettivo

Realizzare un nuovo server MCP locale per analizzare l'utilizzo degli strumenti AI usati nello sviluppo web aziendale, con focus su:

- Codex, quando i dati locali sono disponibili;
- VS Code + estensione GitHub Copilot;
- eventi MCP, skill e hook gestiti dal repository `mcp-servers`;
- modelli e token quando presenti nei log locali;
- nessun salvataggio di prompt, risposte, payload tool o contenuti completi.

Il server deve servire prima di tutto per analytics locali per macchina, evitando il problema delle dashboard online aggregate tra più PC/utenti che usano lo stesso account.

---

## 2. Decisioni consolidate

| Tema | Decisione definitiva |
|---|---|
| Directory server | `analytics-node` |
| Nome MCP config | `analytics-mcp-server` |
| Runtime | Node.js 24+, TypeScript, `dist/index.js` |
| DB | SQLite locale con `sqlite3`, come `memory-node` |
| Env ammessa | Solo `ANALYTICS_DB_PATH` |
| Default DB | `~/.mcp-servers/analytics/analytics.sqlite` |
| Hook log | `dirname(ANALYTICS_DB_PATH)/hooks/events.jsonl` |
| Sorgenti MVP | `codex`, `vscode_copilot`, `vscode_insiders_copilot`, `hook_log` |
| Copilot CLI | Fuori scope |
| Dashboard | Fuori scope |
| Export | Fuori scope |
| Pricing | Fuori MVP, solo appendice post-MVP |
| Contenuti messaggi | Mai salvati |
| Raw JSON sorgenti | Mai salvati |
| Path locali | Non restituiti di default; solo debug esplicito in `analytics_status` |
| Eventi runtime | Solo `mcp`, `skill`, `hook` |
| Tool interni editor | Fuori scope MVP |
| Full-text search | Non prevista nel MVP |
| Scan all'avvio | Vietato |
| Fetch rete durante scan | Vietato |

---

## 3. Fuori scope esplicito

Non implementare nel MVP:

- Copilot CLI;
- Copilot API, GitHub token, OAuth token, file auth;
- dashboard HTTP, Express, React, Vite;
- export JSON/CSV/Markdown/DOCX/XLSX;
- pricing parser, pricing tables, pricing CLI, `analytics_pricing_status`;
- stima token quando i token nativi non sono disponibili;
- salvataggio di prompt, risposte, titoli chat, snippet, payload tool, raw request/response/event;
- ricerca testuale nelle chat;
- lettura contenuti workspace/progetto;
- scan automatico allo startup MCP.

---

## 4. Convenzioni repository

`analytics-node` deve seguire lo stile dei server TypeScript esistenti:

```text
analytics-node/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts
│   ├── config.ts
│   ├── db.ts
│   ├── migrations.ts
│   ├── responses.ts
│   ├── errors.ts
│   ├── privacy.ts
│   ├── scanner.ts
│   ├── sources.ts
│   ├── repo-catalog.ts
│   ├── adapters/
│   │   ├── codex.ts
│   │   ├── vscode-copilot.ts
│   │   └── hook-log.ts
│   └── tools/
│       ├── status.ts
│       ├── scan.ts
│       ├── summary.ts
│       ├── models.ts
│       ├── sessions.ts
│       └── events.ts
├── test/
│   ├── fixtures/
│   └── *.test.mjs
└── README.md
```

`package.json` MVP:

```json
{
  "name": "analytics-node",
  "version": "0.1.0",
  "private": true,
  "description": "Local privacy-first analytics MCP server for Codex and VS Code Copilot usage.",
  "type": "module",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "test": "npm run build && node --test test/*.test.mjs",
    "smoke": "npm run build && node test/smoke.mjs"
  },
  "engines": {
    "node": ">=24"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.26.0",
    "sqlite3": "^5.1.7"
  },
  "devDependencies": {
    "@types/node": "^24.7.2",
    "typescript": "^5.9.3"
  }
}
```

Non aggiungere script pricing nel `package.json` MVP.

---

## 5. Configurazione

### 5.1 Env supportata

Unica env supportata:

```dotenv
ANALYTICS_DB_PATH=/path/to/analytics.sqlite
```

Se non valorizzata:

```text
~/.mcp-servers/analytics/analytics.sqlite
```

Derivati dal DB path:

```text
db_dir = dirname(ANALYTICS_DB_PATH)
hook_log_path = db_dir/hooks/events.jsonl
```

Nessuna altra env analytics deve essere letta.

### 5.2 Path sorgenti

I path dei client devono essere deterministici per OS e risolti da helper interni.

Codex:

```text
~/.codex/sessions/**/*.jsonl
~/.codex/archived_sessions/**/*.jsonl
```

Nota: lo storage reale di Codex Desktop su Windows/Ubuntu va verificato nella milestone adapter Codex. Non introdurre env di override nel MVP.

VS Code/Copilot Windows:

```text
%APPDATA%/Code/User/globalStorage/
%APPDATA%/Code/User/workspaceStorage/
%APPDATA%/Code - Insiders/User/globalStorage/
%APPDATA%/Code - Insiders/User/workspaceStorage/
```

VS Code/Copilot Ubuntu:

```text
~/.config/Code/User/globalStorage/
~/.config/Code/User/workspaceStorage/
~/.config/Code - Insiders/User/globalStorage/
~/.config/Code - Insiders/User/workspaceStorage/
```

Helper richiesti:

```ts
resolveCodexRoots(): string[]
resolveVsCodeCopilotRoots(): string[]
resolveVsCodeInsidersCopilotRoots(): string[]
```

Su Windows usare `process.env.APPDATA`. Su Ubuntu usare `homedir()`.

---

## 6. Privacy e redazione

### 6.1 Regole assolute

Nel DB, nei warning, negli errori, nello stdout/stderr e negli output MCP non devono comparire:

- prompt utente;
- risposta assistant;
- titoli chat;
- snippet codice;
- payload tool completo;
- raw JSON di sessioni VS Code/Codex;
- token, secret, password, cookie, authorization header;
- path completi, salvo `analytics_status(include_local_paths=true)`.

### 6.2 Niente metriche messaggio non necessarie

Nel MVP non salvare:

- `message_hash`;
- `message_chars`;
- `message_bytes`;
- `title_hash`;
- `title_chars`.

Motivo: l'obiettivo MVP è usage analytics su sessioni, modelli, token nativi ed eventi runtime. Per idempotenza dei messaggi basta `session_id + seq`, perché il reimport cancella e reinserisce la sessione.

### 6.3 HMAC locale

Usare HMAC-SHA256 con salt locale per:

- `path_hash`;
- `file_hash`;
- `source_session_hash`;
- `args_hash`;
- eventuali path hash negli output.

Salt salvato in DB nella tabella `settings`:

```text
settings.key = 'hmac_salt'
settings.value = random 32 bytes base64url
```

Scopo: ridurre correlazione/dizionari casuali locali. Non è protezione forte se il DB viene esfiltrato insieme al salt.

### 6.4 Path normalization

Per `path_hash`:

- Windows: path assoluto normalizzato con separator canonico e lowercase;
- Ubuntu/Linux: path assoluto normalizzato, case-sensitive;
- hash sempre HMAC-SHA256 con `hmac_salt`.

### 6.5 Metadata allowlist

Tutte le colonne `metadata_json` e `warnings_json` sono allowlist-only. Vietato salvare oggetti sorgente grezzi.

Helper obbligatori:

```ts
jsonStringifyAllowlisted(tableName, value): string
sanitizeWarning(input): WarningEntry
sanitizeError(error): { code: string; severity: 'warning' | 'error' }
```

In runtime, chiavi non ammesse devono essere scartate e sostituite da warning sanitizzato. Nei test/dev il comportamento può fallire per intercettare regressioni.

### 6.6 Warning shape

Formato unico:

```json
{
  "code": "INVALID_JSON_LINE_SKIPPED",
  "message_code": "INVALID_JSON_LINE_SKIPPED",
  "severity": "warning",
  "source_file_id": "optional-id",
  "details": {
    "line_no": 123
  }
}
```

`message_code` deve essere uguale a `code`. Non usare messaggi liberi.

---

## 7. Enum canonici

### 7.1 Source enum

Valori canonici, sempre con underscore:

```ts
type AnalyticsSource =
  | 'codex'
  | 'vscode_copilot'
  | 'vscode_insiders_copilot'
  | 'hook_log';
```

Nei tool MCP non sono ammessi alias con trattino. Eventuali alias sono ammessi solo in CLI/test helper, non nello schema MCP.

### 7.2 Session source enum

`hook_log` non produce sessioni o messaggi. Quindi:

```ts
type SessionSource =
  | 'codex'
  | 'vscode_copilot'
  | 'vscode_insiders_copilot';
```

### 7.3 Event type enum

```ts
type RuntimeEventType = 'mcp' | 'skill' | 'hook';
```

Niente `editor_tool` nel MVP.

### 7.4 Message role enum

```ts
type MessageRole = 'user' | 'assistant' | 'unknown';
```

Non salvare `system` o `tool` in `message_metrics`. I tool vanno in `runtime_events`.

### 7.5 Session mode enum

```ts
type SessionMode = 'chat' | 'agent' | 'edit' | 'unknown';
```

Valori non riconosciuti devono diventare `unknown`.

---

## 8. Schema SQLite MVP

Tutte le date `*_at` sono ISO UTC `TEXT`. Gli epoch millisecondi usano suffisso `*_ms`.

### 8.1 `settings`

```sql
CREATE TABLE IF NOT EXISTS settings (
  key TEXT PRIMARY KEY CHECK (key IN ('machine_id', 'hmac_salt')),
  value TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
```

`machine_id` è `randomUUID()` generato al primo init. Non usare hostname/utente come chiave primaria.

### 8.2 `schema_migrations`

```sql
CREATE TABLE IF NOT EXISTS schema_migrations (
  version INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  applied_at TEXT NOT NULL
);
```

Le migration stanno in `src/migrations.ts`, non in file SQL separati nel MVP.

### 8.3 `scan_runs`

```sql
CREATE TABLE IF NOT EXISTS scan_runs (
  id TEXT PRIMARY KEY,
  started_at TEXT NOT NULL,
  completed_at TEXT,
  status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'partial', 'failed', 'interrupted')),
  sources_json TEXT NOT NULL,
  dry_run INTEGER NOT NULL DEFAULT 0,
  force INTEGER NOT NULL DEFAULT 0,
  files_seen INTEGER NOT NULL DEFAULT 0,
  files_imported INTEGER NOT NULL DEFAULT 0,
  files_skipped INTEGER NOT NULL DEFAULT 0,
  files_failed INTEGER NOT NULL DEFAULT 0,
  sessions_upserted INTEGER NOT NULL DEFAULT 0,
  messages_upserted INTEGER NOT NULL DEFAULT 0,
  runtime_events_upserted INTEGER NOT NULL DEFAULT 0,
  warnings_json TEXT,
  metadata_json TEXT
);
```

Allowlist `metadata_json`:

```json
{
  "adapter_versions": {
    "codex": "0.1.0",
    "vscode_copilot": "0.1.0",
    "vscode_insiders_copilot": "0.1.0",
    "hook_log": "0.1.0"
  }
}
```

Nessun oggetto annidato libero oltre questa shape.

### 8.4 `source_files`

```sql
CREATE TABLE IF NOT EXISTS source_files (
  id TEXT PRIMARY KEY,
  source TEXT NOT NULL CHECK (source IN ('codex', 'vscode_copilot', 'vscode_insiders_copilot', 'hook_log')),
  path_hash TEXT NOT NULL,
  file_hash TEXT,
  size_bytes INTEGER NOT NULL DEFAULT 0,
  mtime_ms INTEGER,
  first_seen_at TEXT NOT NULL,
  last_seen_at TEXT NOT NULL,
  last_imported_at TEXT,
  last_status TEXT NOT NULL CHECK (last_status IN ('pending', 'imported', 'failed')),
  last_error_code TEXT,
  last_error_severity TEXT CHECK (last_error_severity IN ('warning', 'error')),
  warnings_json TEXT,
  metadata_json TEXT,
  UNIQUE(source, path_hash)
);
```

Non usare `skipped` come `last_status`: se un file invariato viene saltato, mantiene `last_status='imported'` e incrementa solo `scan_runs.files_skipped`.

`last_error_code` enum:

```text
PARSE_ERROR
UNSUPPORTED_FORMAT
REMOTE_WORKSPACE
FILE_READ_ERROR
INVALID_JSON
FILE_TOO_LARGE
UNKNOWN_ERROR
```

### 8.5 `sessions`

```sql
CREATE TABLE IF NOT EXISTS sessions (
  id TEXT PRIMARY KEY,
  source TEXT NOT NULL CHECK (source IN ('codex', 'vscode_copilot', 'vscode_insiders_copilot')),
  source_session_hash TEXT NOT NULL,
  source_file_id TEXT NOT NULL,
  machine_id TEXT NOT NULL,
  project_id TEXT,
  mode TEXT NOT NULL DEFAULT 'unknown' CHECK (mode IN ('chat', 'agent', 'edit', 'unknown')),
  model_primary TEXT,
  token_available INTEGER NOT NULL DEFAULT 0,
  message_count INTEGER NOT NULL DEFAULT 0,
  user_message_count INTEGER NOT NULL DEFAULT 0,
  assistant_message_count INTEGER NOT NULL DEFAULT 0,
  created_at TEXT,
  updated_at TEXT NOT NULL,
  created_at_source TEXT CHECK (created_at_source IN ('native', 'updated_at', 'file_mtime', 'unknown')),
  metadata_json TEXT,
  FOREIGN KEY(source_file_id) REFERENCES source_files(id) ON DELETE CASCADE
);
```

`project_id` può essere `NULL`. Non usare `unknown` condiviso, perché aggrega dati non correlati.

Formato `project_id` solo se disponibile da metadata client senza leggere workspace:

```text
workspace:<hmac>
```

Non leggere `git config` nel MVP. Non leggere contenuti workspace.

`model_primary`:

- massimo 160 caratteri;
- niente newline;
- niente JSON;
- niente path;
- se non strutturato, `NULL`.

### 8.6 `message_metrics`

```sql
CREATE TABLE IF NOT EXISTS message_metrics (
  id TEXT PRIMARY KEY,
  session_id TEXT NOT NULL,
  source_file_id TEXT NOT NULL,
  seq INTEGER NOT NULL,
  role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'unknown')),
  model TEXT,
  input_tokens INTEGER DEFAULT 0,
  output_tokens INTEGER DEFAULT 0,
  cache_read_tokens INTEGER DEFAULT 0,
  cache_write_tokens INTEGER DEFAULT 0,
  token_available INTEGER NOT NULL DEFAULT 0,
  partial_token_data INTEGER NOT NULL DEFAULT 0,
  created_at TEXT,
  timestamp_source TEXT CHECK (timestamp_source IN ('native', 'session', 'file_mtime', 'fallback', 'unknown')),
  metadata_json TEXT,
  FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE,
  FOREIGN KEY(source_file_id) REFERENCES source_files(id) ON DELETE CASCADE,
  UNIQUE(session_id, seq)
);
```

`id` deve essere deterministico:

```text
hmac(machine_id + session_id + seq)
```

Non UUID casuale.

`model` a livello messaggio va valorizzato solo se il log lo espone in modo strutturato. Altrimenti `NULL` e si usa `sessions.model_primary`.

### 8.7 `runtime_events`

```sql
CREATE TABLE IF NOT EXISTS runtime_events (
  id TEXT PRIMARY KEY,
  source TEXT NOT NULL CHECK (source IN ('codex', 'vscode_copilot', 'vscode_insiders_copilot', 'hook_log')),
  source_file_id TEXT NOT NULL,
  session_id TEXT,
  event_type TEXT NOT NULL CHECK (event_type IN ('mcp', 'skill', 'hook')),
  event_origin TEXT NOT NULL CHECK (event_origin IN ('observed_structured', 'hook_hint')),
  event_name TEXT NOT NULL,
  mcp_server_name TEXT,
  tool_name TEXT,
  skill_name TEXT,
  hook_name TEXT,
  hook_event TEXT,
  source_line_no INTEGER,
  event_seq INTEGER NOT NULL DEFAULT 0,
  args_keys_json TEXT,
  args_hash TEXT,
  args_bytes INTEGER DEFAULT 0,
  occurred_at TEXT NOT NULL,
  timestamp_source TEXT CHECK (timestamp_source IN ('native', 'session', 'file_mtime', 'fallback', 'unknown')),
  is_self_event INTEGER NOT NULL DEFAULT 0,
  metadata_json TEXT,
  FOREIGN KEY(source_file_id) REFERENCES source_files(id) ON DELETE CASCADE,
  FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE
);
```

`id` deve essere deterministico, non UUID casuale:

```text
hmac(machine_id + source + source_file_id + session_id? + source_line_no? + event_seq + event_type + event_name + occurred_at)
```

Per hook JSONL `source_line_no` è obbligatorio.  
Per Codex/VS Code, se non esiste linea fisica affidabile, usare `event_seq` progressivo nel file/sessione.

Evento MCP valido richiede:

```text
event_type='mcp'
mcp_server_name non NULL
tool_name non NULL
event_name = `${mcp_server_name}.${tool_name}`
event_origin='observed_structured'
```

`McpHint` da hook non è uso MCP reale:

```text
event_type='hook'
hook_event='McpHint'
event_origin='hook_hint'
metadata_json.kind='mcp_hint'
metadata_json.mcp_server_name='<allowlisted server>'
```

Implementazione dashboard: un `McpHint` puo' valorizzare anche
`runtime_events.mcp_server_name` e, quando il payload hook espone un nome tool
strutturato, `runtime_events.tool_name`. Questi campi sono solo dettaglio di
classificazione UI/report e non trasformano l'hint in chiamata MCP reale:
`event_type` deve restare `hook` e i conteggi `mcp_calls` devono continuare a
considerare solo eventi MCP osservati in modo strutturato.

`SkillHint` è ammesso. `SkillUsed` è fuori scope MVP salvo evento strutturato certo futuro.

Allowlist `runtime_events.metadata_json`:

```json
{
  "kind": "mcp_hint|skill_hint|hook_call",
  "mcp_server_name": "sql-mcp-server",
  "timestamp_source": "fallback"
}
```

---

## 9. Adapter e comportamento sorgenti

### 9.1 Codex adapter

Scopo:

- importare sessioni locali Codex se i file sono disponibili;
- leggere solo dati strutturati necessari;
- non salvare contenuti;
- importare eventi MCP solo da tool call strutturate chiaramente riconducibili ai server MCP allowlisted.

Task obbligatorio M2:

- verificare su macchina reale lo storage Codex Desktop Windows/Ubuntu;
- se non trovato, documentare warning non bloccante;
- non aggiungere env override.

### 9.2 VS Code/Copilot adapter

Supporto MVP:

- VS Code stable;
- VS Code Insiders opzionale;
- estensione GitHub Copilot;
- JSON/JSONL locali in `globalStorage` e `workspaceStorage`.

Remote/WSL/multi-root policy:

- se workspace URI non è `file://`, non leggere workspace remoto;
- importare metriche sessione solo se i dati locali sono indipendenti dal progetto;
- `project_id=NULL`;
- aggiungere warning sanitizzato `REMOTE_WORKSPACE` solo se utile, non rumoroso.

### 9.3 Hook log adapter

`hook_log` produce solo:

- `source_files`;
- `runtime_events`.

Non produce:

- `sessions`;
- `message_metrics`.

---

## 10. Hook logging

### 10.1 Path

```text
hook_log_path = dirname(ANALYTICS_DB_PATH)/hooks/events.jsonl
```

Gli hook devono ricevere il DB path con argomento esplicito:

```bash
node scripts/hooks/sophia-user-prompt-submit.mjs --analytics-db-path "D:\\runtime\\analytics\\analytics.sqlite"
```

Helper:

```ts
resolveAnalyticsDbPath(argv): string {
  return readArg(argv, '--analytics-db-path')
    || process.env.ANALYTICS_DB_PATH
    || defaultAnalyticsDbPath();
}
```

Non introdurre `ANALYTICS_HOOK_LOG_PATH`.

### 10.2 Helper hook standalone

Creare helper leggero:

```text
scripts/hooks/analytics-log.mjs
```

Gli hook non devono importare `analytics-node/dist`, perché potrebbero girare prima della build.

### 10.3 Formato JSONL

Esempi ammessi:

```json
{"ts":"2026-05-27T10:15:02.123Z","event":"SessionStart","hook":"sophia-session-start","matched":true}
{"ts":"2026-05-27T10:16:10.456Z","event":"UserPromptSubmit","hook":"sophia-user-prompt-submit","matched":true,"hints":["docs","memory"]}
{"ts":"2026-05-27T10:17:22.789Z","event":"SkillHint","hook":"sophia-user-prompt-submit","skill":"mcp-technical-analyst","matched":true}
{"ts":"2026-05-27T10:18:22.789Z","event":"McpHint","hook":"sophia-pretool-sql","mcp_server":"sql-mcp-server","matched":true}
```

Vietato nel log:

- prompt;
- tool payload;
- path completi;
- stack trace;
- raw stdin hook.

### 10.4 Append e rotazione

Gli hook fanno solo append best-effort, una riga per evento, senza rotazione.

Lo scanner gestisce rotazione dopo import completato con successo del file attivo:

```text
events.jsonl -> import -> se size > 5 MB -> rename/overwrite events.jsonl.1
```

MVP:

- leggere solo `events.jsonl` attivo;
- `.1` è diagnostico, non sorgente import;
- parser tollerante a righe tronche o JSON invalido;
- righe invalide generano warning sanitizzato, non crash.

---

## 11. Cataloghi MCP / skill / hook

### 11.1 MCP allowlist

Derivare dai generatori/config del repo. MVP minimo:

```text
cf-mcp-server
docs-mcp-server
git-mcp-server
linter-mcp-server
mantis-mcp-server
office-mcp-server
playwright-mcp-server
projectfs-mcp-server
memory-mcp-server
sql-mcp-server
analytics-mcp-server
```

`analytics-mcp-server` è self-event: importabile, ma escluso dai summary default con `is_self_event=1`.

### 11.2 Skill allowlist

Derivare da `skills/*/SKILL.md` quando il repo root è risolto. Non leggere il catalogo allo startup. Funzioni lazy in `src/repo-catalog.ts`.

Fallback se repo root non risolvibile:

- accettare skill core definite staticamente nel codice solo per hint già noti;
- oppure skip con warning sanitizzato `CATALOG_UNAVAILABLE`.

Scelta MVP consigliata: fallback statico minimo per skill usate dagli hook Sophia.

### 11.3 Hook allowlist

Derivare da `scripts/hooks/sophia-*` e dal catalogo hook generato.

### 11.4 Repo root detection

Root valido se contiene tutti questi marker:

```text
AGENTS.md
genera_mcp_json.sh
skills/
almeno una directory *-node
```

Nessuna env `REPO_ROOT`.

---

## 12. Scanner behavior

### 12.1 Startup

All'avvio server MCP:

- caricare config;
- creare directory DB;
- aprire SQLite;
- applicare migrations;
- inizializzare `settings.machine_id` e `settings.hmac_salt`;
- marcare eventuali `scan_runs.status='running'` vecchi come `interrupted`.

Vietato allo startup:

- scan dei client;
- lettura cataloghi pesanti;
- fetch rete;
- pricing;
- attraversamento workspace.

### 12.2 `analytics_scan`

Default `sources` se assente:

```text
codex
vscode_copilot
hook_log
```

`vscode_insiders_copilot` si include solo se richiesto esplicitamente o se root Insiders esiste. Se la root non esiste, skip silenzioso non rumoroso.

`since`:

- input ISO 8601;
- parser server-side converte a epoch ms;
- filtra file candidati confrontando `stat.mtimeMs`;
- non filtra messaggi/eventi dentro i file selezionati.

`force=true`:

- rilegge file anche se `size_bytes + mtime_ms` sono invariati;
- ricalcola `file_hash`.

Incremental scan:

1. stat file;
2. confronto `size_bytes + mtime_ms`;
3. se invariato e `force=false`, skip senza cambiare `source_files.last_status`;
4. se cambiato o force, leggere file;
5. calcolare `file_hash` HMAC;
6. upsert `source_files` con `pending` prima del parse;
7. parse/import;
8. status finale `imported` o `failed`.

### 12.3 `dry_run=true`

Nel tool MCP, il server è già inizializzato, quindi il DB può già esistere. Il divieto dry-run riguarda l'esecuzione del tool:

- non creare/aggiornare `scan_runs`;
- non creare/aggiornare `source_files`;
- non creare/aggiornare `sessions`;
- non creare/aggiornare `message_metrics`;
- non creare/aggiornare `runtime_events`;
- può restituire piano e contatori stimati.

Per testare un dry-run senza file DB, usare helper scanner separato non inizializzato dal server MCP.

### 12.4 Reimport sessioni modificate

Quando una sessione già nota viene reimportata:

1. individuare `session.id` deterministico;
2. cancellare tutti i `message_metrics` collegati a `session_id`;
3. cancellare tutti i `runtime_events` collegati a `session_id`;
4. reinserire messaggi/eventi;
5. aggiornare `sessions.message_count`, `user_message_count`, `assistant_message_count`, `token_available` nella stessa transazione sessione.

Questo evita record stale anche se una sessione cambia file sorgente.

### 12.5 Status scan

`scan_runs.status`:

```text
completed   files_failed = 0
partial     files_failed > 0 e almeno un file importato o saltato valido
failed      nessun file processabile oppure errore DB globale
interrupted scan running precedente trovato all'avvio o prima di un nuovo scan
```

`SCAN_PARTIAL` non deve essere errore MCP bloccante: ritornare `ok=true`, `status='partial'`, warnings.

### 12.6 Transazioni

- transazione breve per file o sessione;
- errore di un file rollbacka solo quel file;
- errori per-file non interrompono l'intera scansione;
- errori DB globali chiudono `scan_run` come `failed` quando possibile.

---

## 13. Limiti performance

Costante interna, non env:

```ts
const MAX_SOURCE_FILE_BYTES = 50 * 1024 * 1024;
```

File più grandi:

- non letti;
- `source_files.last_status='failed'`;
- `last_error_code='FILE_TOO_LARGE'`;
- warning sanitizzato.

Questo limite va implementato già nelle milestone adapter, non nel solo hardening finale.

---

## 14. Tool MCP MVP

Tool registrati:

```text
analytics_status
analytics_scan
analytics_summary
analytics_models
analytics_sessions
analytics_events
```

Nessun tool pricing nel MVP.

### 14.1 Regole schema MCP

- nessun alias con trattino negli enum;
- `additionalProperties: false`;
- ogni array dichiara `items`;
- non usare `required: []`: omettere `required` quando non ci sono campi obbligatori;
- i `default` nello schema sono solo documentativi, il parser server-side deve applicarli;
- `limit` e `offset` sono `integer`, non `number`.

Default server-side per tool paginati:

```text
limit = 100
offset = 0
limit max = 1000
```

### 14.2 Annotation MCP

| Tool | Annotation |
|---|---|
| `analytics_status` | `readOnlyHint: true` |
| `analytics_summary` | `readOnlyHint: true` |
| `analytics_models` | `readOnlyHint: true` |
| `analytics_sessions` | `readOnlyHint: true` |
| `analytics_events` | `readOnlyHint: true` |
| `analytics_scan` | `readOnlyHint: false`, `destructiveHint: false` |

Se supportato dal runtime SDK, documentare idempotenza di `analytics_scan`; in ogni caso lo scan deve essere idempotente per file invariati.

### 14.3 Output MCP standard

Sempre:

```json
{
  "content": [
    { "type": "text", "text": "Analytics scan completed: 12 sessions, 4 events." }
  ],
  "structuredContent": {
    "ok": true,
    "...": "..."
  }
}
```

Errori:

```json
{
  "content": [
    { "type": "text", "text": "Analytics scan failed: validation error." }
  ],
  "structuredContent": {
    "ok": false,
    "error_code": "VALIDATION_ERROR",
    "message_code": "VALIDATION_ERROR",
    "warnings": []
  },
  "isError": true
}
```

Per scan parziale usare `ok=true`, non `isError=true`.

### 14.4 Error code enum

```text
VALIDATION_ERROR
DB_ERROR
SOURCE_NOT_FOUND
PARSER_ERROR
PRICING_DISABLED
CATALOG_UNAVAILABLE
```

`SCAN_PARTIAL` non è errore bloccante.

---

## 15. Tool contracts

### 15.1 `analytics_status`

Input:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "include_local_paths": {
      "type": "boolean",
      "description": "Default false. Se true restituisce path locali completi per debug."
    }
  }
}
```

Output default:

```json
{
  "ok": true,
  "server": { "name": "analytics-node", "version": "0.1.0" },
  "db": {
    "available": true,
    "db_path_hash": "...",
    "hook_log_path_hash": "..."
  },
  "features": {
    "pricing": false,
    "dashboard": false,
    "export": false
  },
  "counts": {
    "sessions": 0,
    "runtime_events": 0
  },
  "warnings": []
}
```

Con `include_local_paths=true`, può aggiungere:

```json
{
  "db_path": "...",
  "hook_log_path": "..."
}
```

Solo `analytics_status` può restituire path completi e solo su richiesta esplicita.

### 15.2 `analytics_scan`

Input:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["codex", "vscode_copilot", "vscode_insiders_copilot", "hook_log"]
      }
    },
    "since": {
      "type": "string",
      "description": "ISO 8601. Filtra i file candidati tramite filesystem mtime."
    },
    "force": { "type": "boolean" },
    "dry_run": { "type": "boolean" }
  }
}
```

Non contiene `include_local_paths`.

### 15.3 `analytics_summary`

Input:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["codex", "vscode_copilot", "vscode_insiders_copilot", "hook_log"]
      }
    },
    "date_from": { "type": "string" },
    "date_to": { "type": "string" },
    "group_by": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["source", "project", "model", "day", "event_type", "event_name"]
      }
    }
  }
}
```

Date semantics:

- session metrics filtrano `sessions.updated_at`;
- event metrics filtrano `runtime_events.occurred_at`;
- se entrambi sono richiesti, filtri separati e output separato.

Output separato:

```json
{
  "session_totals": { "sessions": 10, "messages": 100 },
  "event_totals": { "runtime_events": 30 },
  "notes": ["hook_log is included only in event_totals"]
}
```

`hook_log` è ignorato nei `session_totals`.

### 15.4 `analytics_models`

Input sources solo session-based:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["codex", "vscode_copilot", "vscode_insiders_copilot"]
      }
    },
    "date_from": { "type": "string" },
    "date_to": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 1000 },
    "offset": { "type": "integer", "minimum": 0 }
  }
}
```

Output deve distinguere:

```json
{
  "models": [
    {
      "model": "gpt-...",
      "sessions": 10,
      "messages": 80,
      "input_tokens": 1000,
      "output_tokens": 500,
      "token_available_count": 60,
      "token_missing_count": 20,
      "partial_token_data": true
    }
  ]
}
```

Non stimare token mancanti.

### 15.5 `analytics_sessions`

Input:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["codex", "vscode_copilot", "vscode_insiders_copilot"]
      }
    },
    "date_from": { "type": "string" },
    "date_to": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 1000 },
    "offset": { "type": "integer", "minimum": 0 }
  }
}
```

Nessun filtro testuale o titolo nel MVP.

Ordinamento stabile:

```sql
ORDER BY updated_at DESC, id ASC
```

Output paginato:

```json
{
  "total": 120,
  "limit": 100,
  "offset": 0,
  "has_more": true,
  "items": []
}
```

### 15.6 `analytics_events`

Input:

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["codex", "vscode_copilot", "vscode_insiders_copilot", "hook_log"]
      }
    },
    "event_type": {
      "type": "string",
      "enum": ["mcp", "skill", "hook"]
    },
    "date_from": { "type": "string" },
    "date_to": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 1000 },
    "offset": { "type": "integer", "minimum": 0 }
  }
}
```

Non contiene `include_local_paths`.

Ordinamento:

```sql
ORDER BY occurred_at DESC, id ASC
```

---

## 16. Parsing e sicurezza runtime

### 16.1 No raw in memoria persistente

Gli adapter possono leggere/ricostruire dati in memoria solo quanto necessario per estrarre metriche. Vietato:

- loggare oggetti ricostruiti;
- salvare oggetti ricostruiti;
- includerli in errori/warnings;
- stamparli su stdout/stderr.

### 16.2 VS Code JSONL

Preferire parser mirato/streaming. Se serve ricostruzione stato completa in memoria, aggiungere test che verifica:

- nessun raw message nel DB;
- nessun raw message in warning;
- nessun raw message in errore;
- nessuna stampa su stdout/stderr in condizioni normali.

### 16.3 Token auth

L'adapter VS Code/Copilot non deve leggere:

- token GitHub;
- OAuth token;
- file `apps.json` GitHub Copilot;
- credential store;
- API GitHub/Copilot.

---

## 17. Integrazione repo

File da aggiornare nel MVP:

```text
genera_mcp_json.sh
genera_mcp_json.ps1
scripts/install-user-runtime.js
scripts/generate-codex-hooks.js
analytics-node/README.md
docs/server-capability-matrix.md
```

Hook logging:

```text
scripts/hooks/analytics-log.mjs
scripts/hooks/sophia-*.mjs dove serve log minimale
```

Se Antigravity hooks non sono MVP, non modificarli. Per MVP applicare hook logging solo ai generatori Codex se gli hook Sophia sono usati da Codex.

### 17.1 Generatori MCP

Aggiungere server:

```js
{
  name: 'analytics-mcp-server',
  dir: 'analytics-node',
  args: ['dist/index.js'],
  ...(analyticsEnv ? { env: analyticsEnv } : {})
}
```

`analyticsEnv` contiene solo `ANALYTICS_DB_PATH` se valorizzata nel processo generatore.

### 17.2 Hook generator

Aggiornare `scripts/generate-codex-hooks.js` per passare:

```text
--analytics-db-path <resolved analytics db path>
```

Test obbligatorio con path Windows contenente spazi:

```text
D:\MCP Runtime\analytics\analytics.sqlite
```

Se il formato hook supporta solo `command` stringa, il generatore deve fare escaping robusto e testato. Non dichiarare supporto array args se non verificato.

### 17.3 Capability matrix

Bozza riga:

```text
analytics-node | yes | no | no | no | no | Server low-level | no (tool separati) | no | no | buono | SQLite locale privacy-first per usage analytics Codex/VS Code Copilot/hook; no contenuti, no export, no dashboard, pricing post-MVP.
```

`action`: `no (tool separati)`.

### 17.4 AGENTS.md

Non aggiornare `AGENTS.md` salvo decisione esplicita. La documentazione principale resta:

```text
analytics-node/README.md
docs/analytics-mcp-server-analysis.md se inserito nel repo
```

---

## 18. Porting Agentlytics

Usare Agentlytics come riferimento concettuale per:

- adapter Codex;
- adapter VS Code/Copilot;
- cache SQLite;
- statistiche modelli/token/tool.

Non importare:

- dashboard;
- server HTTP;
- relay;
- Deno;
- Copilot CLI;
- pricing automatico nel MVP.

Licenza:

- se si copia codice sostanziale, aggiungere `analytics-node/NOTICE.md` con attribution e licenza ISC;
- se si reimplementano solo concetti, citare Agentlytics in `analytics-node/README.md` come riferimento tecnico, senza codice copiato.

---

## 19. Pricing post-MVP

Il MVP non registra tool/script/tabelle pricing.

Appendice post-MVP, non implementare finché usage analytics non è stabile:

- script manuale `scripts/update-pricing.js`;
- tabelle `pricing_snapshots`, `model_token_prices`, `copilot_plan_pricing`;
- nessun fetch durante scan/startup;
- nessuna rete nei test standard;
- fallback import manuale se scraping fallisce;
- update-pricing fallisce senza modificare snapshot esistenti se parser o fetch falliscono.

Fonti future:

- GitHub usage-based billing per AI Credits e Copilot Pro+;
- GitHub models and pricing per prezzi token Copilot;
- OpenAI API Pricing per confronto token-cost pay-per-use.

Snapshot futuri devono salvare:

```text
fetched_at
effective_from
source_url
source_hash
parser_version
```

---

## 20. Test obbligatori

### 20.1 Privacy

Fixture per ogni adapter con sentinelle:

```text
SHOULD_NOT_BE_SAVED_PROMPT
SHOULD_NOT_BE_SAVED_RESPONSE
SHOULD_NOT_BE_SAVED_TOOL_ARGS
SHOULD_NOT_BE_SAVED_PATH
SHOULD_NOT_BE_SAVED_TOKEN
```

Test:

```text
privacy_sqlite_dump_does_not_contain_fixture_secrets
privacy_metadata_json_allowlist_only
privacy_warnings_json_template_only
privacy_no_raw_json_saved
privacy_no_raw_parser_error_saved
privacy_no_stdout_stderr_raw_content
```

### 20.2 Schema MCP

```text
schema_arrays_have_items
schema_no_required_empty_array
schema_no_dash_aliases
schema_defaults_applied_server_side
schema_rejects_unknown_properties
schema_group_by_enum
schema_events_event_type_enum
```

### 20.3 Scanner

```text
server_startup_does_not_scan
server_startup_marks_running_scan_as_interrupted
scan_dry_run_does_not_write_tool_data
scan_idempotent_same_fixture
scan_incremental_skips_unchanged_file
scan_force_rereads_unchanged_file
scan_modified_session_replaces_old_messages_and_events
scan_corrupt_file_records_failed_without_aborting_run
scan_partial_returns_ok_with_warnings
scan_does_not_fetch_network
scan_does_not_import_pricing_modules
```

### 20.4 Paths

```text
path_hash_windows_case_insensitive
path_hash_linux_case_sensitive
status_redacts_paths_by_default
status_include_local_paths_true_returns_paths
hook_generator_windows_path_with_spaces
```

### 20.5 Adapters

```text
codex_fixture_imports_sessions_without_content
codex_desktop_storage_validation_documented
vscode_fixture_imports_sessions_without_content
vscode_remote_workspace_policy_applied
hook_log_imports_events_only_no_sessions
hook_log_invalid_lines_are_warnings
hook_log_dedup_by_deterministic_id
hook_helper_writes_minimal_jsonl
```

### 20.6 Outputs

```text
sessions_pagination_stable_order
sessions_source_excludes_hook_log
models_source_excludes_hook_log
models_reports_token_missing_count
summary_splits_session_totals_and_event_totals
summary_hook_log_excluded_from_session_totals
events_pagination_stable_order
mcp_hint_is_hook_event_not_mcp_usage
skill_hint_is_not_skill_used
analytics_self_events_excluded_from_default_summary
```

### 20.7 No scope drift

```text
no_copilot_cli_source_registered
no_dashboard_dependencies
no_export_tool_registered
no_pricing_tool_registered
no_pricing_script_in_package_mvp
no_token_auth_files_read
runtime_events_only_mcp_skill_hook
```

---

## 21. Milestone implementative

### M0 - Scaffold e DB baseline

File:

```text
analytics-node/package.json
analytics-node/tsconfig.json
analytics-node/src/config.ts
analytics-node/src/db.ts
analytics-node/src/migrations.ts
analytics-node/src/privacy.ts
analytics-node/src/index.ts
analytics-node/test/*.test.mjs
```

Deliverable:

- server MCP avviabile;
- `analytics_status`;
- DB migrations;
- `settings.machine_id` e `settings.hmac_salt`;
- nessuno scan automatico.

Test obbligatori:

```text
fresh_db_schema
server_startup_does_not_scan
status_redacts_paths_by_default
schema_arrays_have_items
```

### M1 - Scanner core e source_files

File:

```text
src/scanner.ts
src/sources.ts
src/errors.ts
src/responses.ts
src/tools/scan.ts
```

Deliverable:

- `analytics_scan`;
- `scan_runs`;
- `source_files`;
- dry-run;
- incremental scan;
- force;
- partial status.

Test obbligatori:

```text
scan_dry_run_does_not_write_tool_data
scan_idempotent_same_fixture
scan_incremental_skips_unchanged_file
scan_corrupt_file_records_failed_without_aborting_run
```

### M2 - Codex adapter

File:

```text
src/adapters/codex.ts
test/fixtures/codex/*
```

Deliverable:

- import sessioni Codex;
- import token/modello se strutturati;
- import MCP events solo se strutturati e allowlisted;
- validazione storage Codex Desktop documentata.

Test obbligatori:

```text
codex_fixture_imports_sessions_without_content
privacy_sqlite_dump_does_not_contain_fixture_secrets
codex_desktop_storage_validation_documented
```

### M3 - VS Code/Copilot adapter

File:

```text
src/adapters/vscode-copilot.ts
test/fixtures/vscode/*
```

Deliverable:

- VS Code stable;
- VS Code Insiders se richiesto;
- path Windows/Ubuntu;
- remote workspace policy;
- max file size.

Test obbligatori:

```text
vscode_fixture_imports_sessions_without_content
vscode_remote_workspace_policy_applied
path_hash_windows_case_insensitive
path_hash_linux_case_sensitive
```

### M4 - Hook log e runtime events

File:

```text
scripts/hooks/analytics-log.mjs
src/adapters/hook-log.ts
src/repo-catalog.ts
src/tools/events.ts
```

Deliverable:

- hook JSONL minimal;
- import solo runtime events;
- no sessioni/messaggi da hook_log;
- `SkillHint` e `McpHint` gestiti correttamente;
- self-event analytics marcato/escluso.

Test obbligatori:

```text
hook_log_imports_events_only_no_sessions
mcp_hint_is_hook_event_not_mcp_usage
skill_hint_is_not_skill_used
analytics_self_events_excluded_from_default_summary
```

### M5 - Query tools

File:

```text
src/tools/summary.ts
src/tools/models.ts
src/tools/sessions.ts
src/tools/events.ts
```

Deliverable:

- `analytics_summary`;
- `analytics_models`;
- `analytics_sessions`;
- `analytics_events`;
- pagination stabile;
- output `content` + `structuredContent`.

Test obbligatori:

```text
sessions_pagination_stable_order
models_reports_token_missing_count
summary_splits_session_totals_and_event_totals
events_pagination_stable_order
```

### M6a - Generatori MCP

File:

```text
genera_mcp_json.sh
genera_mcp_json.ps1
```

Deliverable:

- aggiunta `analytics-mcp-server`;
- solo `ANALYTICS_DB_PATH` se valorizzata;
- nessuna env extra.

### M6b - Hook generator

File:

```text
scripts/generate-codex-hooks.js
```

Deliverable:

- passaggio `--analytics-db-path` agli hook Sophia;
- test path Windows con spazi;
- nessun quoting rotto.

### M6c - Installer e docs

File:

```text
scripts/install-user-runtime.js
analytics-node/README.md
docs/server-capability-matrix.md
```

Deliverable:

- build/test include `analytics-node`;
- documentazione locale;
- capability matrix aggiornata;
- `AGENTS.md` non modificato salvo decisione esplicita.

### M7 - Hardening

Deliverable:

- performance limit già presente negli adapter;
- smoke completo;
- no scope drift;
- no network;
- no raw DB.

---

## 22. Guardrail finali per Codex

Non introdurre:

```text
Copilot CLI
analytics_export
analytics_costs
analytics_pricing_status
pricing tables
pricing scripts
Express
React
Vite
Deno
HTTP dashboard
raw content fields
message_hash/message_chars/message_bytes nel MVP
content/title/prompt/response/snippet columns
args_json raw
editor_tool event type
hook_log sessions
hook_log message_metrics
full-text search
query text search
extra env oltre ANALYTICS_DB_PATH
API/token/auth file reads
network fetch during scan/startup/tests
```

Obbligatorio:

```text
MCP schemas strict
arrays con items
no required: []
default server-side
structuredContent sempre presente
path redatti di default
warning sanitizzati
eventi deterministici
scan idempotente
privacy tests con sentinelle
no pricing MVP
milestone piccole
```

---

## 23. Prompt operativi per Codex

### Prompt M0

```text
Implementa solo M0 dello spec analytics-node: scaffold TypeScript, DB SQLite, migrations, settings machine_id/hmac_salt, analytics_status. Non implementare scan, adapter, pricing, dashboard, export. Esegui test M0 obbligatori.
```

### Prompt M1

```text
Implementa solo M1: scanner core, scan_runs, source_files, analytics_scan con dry_run, since, force, incremental scan, partial status. Non implementare adapter reali oltre fixture fake. Non introdurre pricing/export/dashboard. Esegui test M1 obbligatori.
```

### Prompt M2

```text
Implementa solo M2: Codex adapter con fixture, import sessioni/message_metrics senza contenuti, token/modello solo se strutturati, MCP events solo se strutturati e allowlisted. Verifica/documenta storage Codex Desktop. Esegui test M2 obbligatori.
```

### Prompt M3

```text
Implementa solo M3: VS Code/Copilot adapter stable e Insiders opzionale, path Windows/Ubuntu, remote workspace policy, max file size. Vietato leggere token/auth GitHub/Copilot. Esegui test M3 obbligatori.
```

### Prompt M4

```text
Implementa solo M4: hook log helper standalone, hook_log adapter, runtime_events per mcp/skill/hook. hook_log non crea sessions/message_metrics. SkillHint sì, SkillUsed no. McpHint è evento hook, non uso MCP. Esegui test M4 obbligatori.
```

### Prompt M5

```text
Implementa solo M5: analytics_summary, analytics_models, analytics_sessions, analytics_events con output paginati, filtri, ordinamento stabile e structuredContent. Non aggiungere export/pricing/dashboard. Esegui test M5 obbligatori.
```

### Prompt M6a

```text
Implementa solo M6a: aggiorna genera_mcp_json.sh e genera_mcp_json.ps1 per analytics-mcp-server. Solo ANALYTICS_DB_PATH se presente. Nessuna env extra. Esegui test generatori.
```

### Prompt M6b

```text
Implementa solo M6b: aggiorna generate-codex-hooks.js per passare --analytics-db-path agli hook Sophia e aggiungi test Windows path con spazi. Non modificare Antigravity salvo richiesta esplicita.
```

### Prompt M6c

```text
Implementa solo M6c: install-user-runtime, README analytics-node, server-capability-matrix. Non modificare AGENTS.md salvo istruzione esplicita. Esegui smoke completo.
```

---

## 24. Criteri di accettazione MVP

Il MVP è accettabile quando:

1. `analytics-mcp-server` si avvia via `dist/index.js`.
2. Usa solo `ANALYTICS_DB_PATH`.
3. Non scansiona nulla allo startup.
4. `analytics_status` funziona e redige path di default.
5. `analytics_scan` è idempotente.
6. File corrotti/non supportati non interrompono l'intero scan.
7. `hook_log` non crea sessioni o messaggi.
8. Il DB non contiene prompt, risposte, titoli, snippet, raw JSON o token auth.
9. Gli output MCP usano `content` + `structuredContent`.
10. Tutti gli schema sono compatibili con client MCP severi.
11. Non esistono tool export/dashboard/pricing nel MVP.
12. Non viene letta alcuna API o file auth GitHub/Copilot.
13. I test privacy, idempotenza, no-network e scope drift passano.
14. La capability matrix è aggiornata.
15. La documentazione locale è aggiornata.


---

## 25. Estensioni post-MVP: compatibilita' Claude, Gemini e Antigravity

Questa sezione estende la roadmap senza modificare il perimetro MVP gia' definito. Le integrazioni devono essere aggiunte come adapter separati, dopo stabilizzazione di Codex, VS Code/Copilot e hook log.

Obiettivo: supportare altri client AI compatibili con workflow MCP/agentici, seguendo la stessa impostazione privacy-first gia' prevista per Codex:

- scansione locale di cartelle note del client;
- decodifica ad hoc del formato sessione/chat;
- nessun salvataggio di prompt, risposte, titoli, snippet, raw JSON, payload tool o token;
- modelli/token/eventi MCP solo se disponibili in modo strutturato;
- adapter disabilitabile se il formato non e' verificato con fixture reali;
- nessuna regressione sui parser MVP esistenti.

### 25.1 Nuove sorgenti post-MVP

I nuovi valori sorgente devono usare underscore e non alias con trattino:

```ts
type ExtendedAnalyticsSource =
  | 'claude_code'
  | 'claude_desktop'
  | 'gemini_cli'
  | 'google_antigravity';
```

Questi valori non entrano negli enum MVP. Vanno aggiunti solo tramite migration post-MVP, aggiornando in modo coerente:

- schema SQLite;
- input schema MCP;
- scanner;
- test privacy;
- documentazione;
- capability matrix.

### 25.2 Claude Code / Claude Desktop

Candidate roots da validare con test reali Windows/Ubuntu prima di abilitare l'adapter:

```text
# Claude Code CLI, candidate comune
~/.claude/projects/**/*.jsonl
~/.claude/**/*.jsonl

# Windows equivalente
%USERPROFILE%/.claude/projects/**/*.jsonl
%USERPROFILE%/.claude/**/*.jsonl

# Claude Desktop, candidate da verificare
%APPDATA%/Claude/
~/.config/Claude/
```

Regole adapter:

- `claude_code` e `claude_desktop` restano sorgenti distinte;
- se Claude Desktop non salva chat localmente in formato leggibile o usa storage cifrato/proprietario, l'adapter deve restituire warning `UNSUPPORTED_FORMAT` e non tentare bypass;
- nessuna lettura di token Anthropic, file auth, cookie, keychain o credential store;
- nessuna chiamata API Anthropic;
- se sono presenti tool call MCP strutturate, importarle solo se `mcp_server_name` e `tool_name` sono allowlisted;
- se il formato non espone token/modello in modo strutturato, lasciare `token_available=0` e `model_primary=NULL`.

Test minimi post-MVP:

```text
claude_code_fixture_imports_sessions_without_content
claude_code_fixture_imports_structured_mcp_events_only
claude_desktop_unsupported_storage_is_warning_not_failure
claude_adapters_do_not_read_auth_or_token_files
```

### 25.3 Gemini CLI

Candidate roots da validare con test reali Windows/Ubuntu prima di abilitare l'adapter:

```text
# Linux/Ubuntu, candidate
~/.gemini/
~/.gemini/**/history*
~/.gemini/**/*.json
~/.gemini/**/*.jsonl

# Windows, candidate
%USERPROFILE%/.gemini/
%USERPROFILE%/.gemini/**/history*
%USERPROFILE%/.gemini/**/*.json
%USERPROFILE%/.gemini/**/*.jsonl
```

Regole adapter:

- sorgente canonica: `gemini_cli`;
- non assumere il formato file prima della validazione con fixture reali;
- importare solo metriche/sessioni/eventi strutturati;
- non leggere credential, OAuth, token Google, cache browser, keychain o account store;
- se i log contengono riferimenti a MCP, registrarli solo se riconducibili a tool call strutturata e server MCP allowlisted;
- niente fetch rete, niente interrogazioni Google API.

Test minimi post-MVP:

```text
gemini_cli_fixture_imports_sessions_without_content
gemini_cli_unsupported_or_missing_storage_is_warning_not_failure
gemini_cli_does_not_read_google_auth_files
gemini_cli_mcp_events_require_structured_tool_call
```

### 25.4 Google Antigravity

Candidate roots da validare con test reali Windows/Ubuntu prima di abilitare l'adapter:

```text
# Linux/Ubuntu, candidate coerenti con runtime Gemini/Antigravity
~/.gemini/antigravity/
~/.gemini/config/
~/.gemini/agents/

# Windows, candidate coerenti con runtime Gemini/Antigravity
%USERPROFILE%/.gemini/antigravity/
%USERPROFILE%/.gemini/config/
%USERPROFILE%/.gemini/agents/
```

Nota: questi path sono candidate roots di runtime/configurazione. Non implicano che le chat siano salvate li'. L'adapter deve prima scoprire e validare il formato sessione effettivo con fixture reali.

Regole adapter:

- sorgente canonica: `google_antigravity`;
- non confondere skill/agent/plugin installati con sessioni/chat effettive;
- se Antigravity espone hook o eventi strutturati, importarli come `runtime_events` solo se compatibili con le regole privacy e allowlist;
- nessuna modifica ai generatori hook Antigravity nel MVP analytics; eventuale supporto Antigravity va progettato come milestone post-MVP separata;
- rispettare i vincoli gia' documentati nel repo su path senza spazi/quoting se si estendono generatori Antigravity.

Test minimi post-MVP:

```text
antigravity_fixture_imports_sessions_without_content
antigravity_config_dirs_are_not_misclassified_as_chats
antigravity_events_require_structured_format
antigravity_adapter_does_not_require_generator_changes
```

### 25.5 Milestone post-MVP suggerite

```text
M8a - Adapter Claude Code
M8b - Adapter Claude Desktop, solo se storage locale leggibile e verificato
M8c - Adapter Gemini CLI
M8d - Adapter Google Antigravity
M8e - Query tools aggiornati con nuove source enum
M8f - Smoke completo privacy/no-network/scope drift su tutte le source estese
```

Ogni adapter deve essere sviluppato in PR separata, con fixture proprie e test privacy dedicati. Non introdurre piu' di una nuova sorgente per PR.

### 25.6 Guardrail anti-regressione per nuove sorgenti

Ogni nuova sorgente deve rispettare gli stessi vincoli MVP:

```text
no prompt
no response
no title
no snippets
no raw JSON
no payload tool completo
no token/auth/cookie/credential store
no API/fetch rete
no workspace content scan
no source enum con trattino
no eventi fuori da mcp/skill/hook
no token estimation
no dashboard/export/pricing
```

Se il formato locale non consente estrazione privacy-safe, l'adapter deve restare `unsupported` e non va aggirato.

---

## 26. Funzionalita' post-MVP: cancellazione dati importati

Aggiungere una funzione di cancellazione controllata per rimuovere dal DB analytics dati gia' importati, senza toccare i file originali dei client.

La cancellazione e' post-MVP rispetto alla specifica iniziale, ma va progettata prima di estendere molte sorgenti, per poter rimuovere import errati o dati non piu' desiderati.

### 26.1 Tool MCP proposto

```text
analytics_delete_imported
```

Annotation:

```text
readOnlyHint: false
destructiveHint: true
```

Regole:

- non cancella file sorgenti reali di Codex, VS Code, Claude, Gemini o Antigravity;
- cancella solo righe del DB analytics;
- richiede sempre dry-run o conferma esplicita;
- non accetta filtri testuali su contenuti chat;
- non espone path locali negli output;
- produce `content` breve + `structuredContent` stabile.

### 26.2 Input schema proposto

```json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "codex",
          "vscode_copilot",
          "vscode_insiders_copilot",
          "hook_log",
          "claude_code",
          "claude_desktop",
          "gemini_cli",
          "google_antigravity"
        ]
      }
    },
    "date_from": {
      "type": "string",
      "description": "ISO 8601 UTC/local ISO convertito server-side."
    },
    "date_to": {
      "type": "string",
      "description": "ISO 8601 UTC/local ISO convertito server-side."
    },
    "period_type": {
      "type": "string",
      "enum": ["day", "month", "year"]
    },
    "period_value": {
      "type": "string",
      "description": "day=YYYY-MM-DD, month=YYYY-MM, year=YYYY."
    },
    "delete_scope": {
      "type": "string",
      "enum": ["all", "sessions", "runtime_events", "source_files"]
    },
    "dry_run": {
      "type": "boolean",
      "description": "Default true. Se true non cancella nulla."
    },
    "confirm_delete": {
      "type": "boolean",
      "description": "Deve essere true per eseguire cancellazione reale."
    },
    "confirm_plan_id": {
      "type": "string",
      "description": "ID piano restituito dal dry-run."
    }
  }
}
```

Default server-side:

```text
dry_run = true
delete_scope = all
```

Non usare `required: []`. Applicare default nel parser server-side.

### 26.3 Modalita' a due step

Step 1, dry-run:

```json
{
  "sources": ["codex"],
  "period_type": "month",
  "period_value": "2026-05",
  "dry_run": true
}
```

Output:

```json
{
  "ok": true,
  "dry_run": true,
  "delete_plan_id": "...",
  "matched": {
    "sessions": 42,
    "message_metrics": 380,
    "runtime_events": 91,
    "source_files": 12
  },
  "warnings": []
}
```

Step 2, esecuzione:

```json
{
  "confirm_plan_id": "...",
  "confirm_delete": true,
  "dry_run": false
}
```

Il piano deve essere ricalcolato prima della cancellazione e confrontato con il piano originale. Se i conteggi cambiano, fallire con `DELETE_PLAN_CHANGED` e richiedere nuovo dry-run.

### 26.4 Filtri temporali

Regole:

- per sorgenti session-based (`codex`, `vscode_copilot`, `vscode_insiders_copilot`, `claude_code`, `claude_desktop`, `gemini_cli`, `google_antigravity`), filtrare sessioni su `sessions.updated_at`;
- per `hook_log`, filtrare eventi su `runtime_events.occurred_at`;
- `date_from/date_to` e `period_type/period_value` sono alternativi. Se entrambi presenti, ritornare `VALIDATION_ERROR`;
- `period_type=day`: `YYYY-MM-DD`, range [00:00:00, giorno successivo);
- `period_type=month`: `YYYY-MM`, range primo giorno mese -> primo giorno mese successivo;
- `period_type=year`: `YYYY`, range 1 gennaio -> 1 gennaio anno successivo.

### 26.5 Semantica cancellazione

`delete_scope='sessions'`:

- cancella `sessions` filtrate;
- cascade o delete esplicito su `message_metrics` collegate;
- cancella `runtime_events` collegati a quelle sessioni;
- non cancella eventi `hook_log` senza sessione.

`delete_scope='runtime_events'`:

- cancella solo `runtime_events` filtrati;
- non cancella sessioni o messaggi.

`delete_scope='source_files'`:

- cancella `source_files` solo se non hanno piu' sessioni/eventi collegati;
- se hanno record collegati, ritornare warning `SOURCE_FILE_STILL_REFERENCED`.

`delete_scope='all'`:

- cancella sessioni/messaggi/eventi filtrati;
- rimuove `source_files` rimasti orfani per le sorgenti filtrate;
- non modifica `settings`, `schema_migrations`, `scan_runs` storici, `delete_runs`.

### 26.6 Audit cancellazioni

Aggiungere tabella post-MVP:

```sql
CREATE TABLE IF NOT EXISTS delete_runs (
  id TEXT PRIMARY KEY,
  plan_id TEXT NOT NULL,
  started_at TEXT NOT NULL,
  completed_at TEXT,
  status TEXT NOT NULL CHECK (status IN ('planned', 'completed', 'failed', 'cancelled')),
  dry_run INTEGER NOT NULL DEFAULT 1,
  sources_json TEXT NOT NULL,
  filters_json TEXT NOT NULL,
  matched_json TEXT NOT NULL,
  deleted_json TEXT,
  warnings_json TEXT
);
```

`filters_json`, `matched_json`, `deleted_json`, `warnings_json` sono generati server-side e allowlist-only. Non salvare query raw utente oltre enum/date normalizzate.

### 26.7 Test obbligatori cancellazione

```text
delete_dry_run_does_not_delete
_delete_requires_confirm_delete_true
delete_requires_matching_plan_id
delete_plan_changed_blocks_execution
delete_codex_by_day
delete_vscode_by_month
delete_hook_log_by_year
delete_all_by_source
delete_scope_sessions_does_not_delete_unrelated_hook_events
delete_scope_source_files_only_orphans
delete_never_deletes_client_files
delete_outputs_no_local_paths
delete_filters_reject_text_queries
delete_period_and_date_filters_are_mutually_exclusive
```

---

## 27. Aggiornamento documentazione e developer guide

Ogni estensione successiva al MVP deve aggiornare la documentazione locale prima di considerare chiusa la milestone.

### 27.1 File documentali da aggiornare

Per il MVP analytics:

```text
analytics-node/README.md
docs/server-capability-matrix.md
README.md, solo se serve menzionare il nuovo server nel bootstrap generale
```

Per adapter post-MVP Claude/Gemini/Antigravity:

```text
analytics-node/README.md
docs/server-capability-matrix.md
docs/future-backlog-mcp-skills.md, se la milestone non viene implementata subito
```

Per funzionalita' di cancellazione:

```text
analytics-node/README.md
docs/server-capability-matrix.md
```

Per guide operative agentiche:

```text
docs/developer-guide-agentic-programming.md
```

se e solo se la guida contiene gia' sezioni su uso di Codex/Copilot/MCP nel workflow quotidiano. In caso contrario, non creare rumore documentale.

`AGENTS.md` deve restare sintetico e non va aggiornato salvo decisione esplicita.

### 27.2 Contenuti minimi README analytics-node

```text
- scopo del server analytics;
- sorgenti supportate e stato: MVP / post-MVP / unsupported;
- privacy policy: cosa non viene mai salvato;
- path DB e unica env ANALYTICS_DB_PATH;
- comandi build/test/smoke;
- tool MCP disponibili;
- comportamento scan/dry-run/force/since;
- comportamento hook_log;
- cancellazione dati importati, quando disponibile;
- troubleshooting path Windows/Ubuntu;
- come aggiungere un nuovo adapter senza regressioni.
```

### 27.3 Developer guide: sezione consigliata

Aggiungere o aggiornare una sezione breve:

```text
Analytics MCP per uso AI locale
```

Contenuti:

- quando usare `analytics-mcp-server`;
- differenza tra analytics locale e dashboard online aggregate;
- cosa chiedere a Codex/Copilot: summary, modelli, eventi MCP/skill/hook;
- cosa non chiedere: contenuti chat, prompt, risposte, export documentali diretti;
- come validare una nuova sorgente con fixture privacy;
- workflow consigliato per cancellare dati importati se un adapter importa dati errati.

### 27.4 Done criteria documentazione

Una milestone analytics e' chiusa solo se:

```text
codice implementato
test obbligatori passano
README locale aggiornato
capability matrix aggiornata
se cambia workflow sviluppatore, developer guide aggiornata o motivazione scritta per non aggiornarla
nessun aggiornamento AGENTS.md salvo decisione esplicita
```

---

## 28. Skill operativa: `mcp-analytics-operator`

Questa sezione va aggiunta in coda per evitare modifiche invasive alle sezioni precedenti. Si applica allo stato del branch `rework`, dove si considerano gia' disponibili:

- cancellazione controllata dei dati importati;
- sorgenti Claude;
- sorgenti Gemini / Antigravity;
- aggiornamento documentazione collegata.

La skill deve seguire lo stile sintetico delle skill operative esistenti, come `mcp-docs-navigator` e `mcp-memory-operator`: quick routing chiaro, regole permanenti, workflow base, contratto tool e riferimenti caricabili solo quando servono.

### 28.1 File skill da aggiungere

Aggiungere questi file:

```text
skills/mcp-analytics-operator/SKILL.md
skills/mcp-analytics-operator/references/analytics-workflow.md
skills/mcp-analytics-operator/references/privacy-rules.md
skills/mcp-analytics-operator/references/deletion-playbook.md
```

Non aggiungere:

```text
skills/mcp-analytics-operator/agents/openai.yaml
```

Non aggiungere test automatici sui contenuti testuali della skill. La validazione funzionale resta sui tool `analytics-node` e sui workflow documentati.

### 28.2 `skills/mcp-analytics-operator/SKILL.md`

```md
---
name: mcp-analytics-operator
description: >
  Operate analytics-node safely for local AI usage analytics across Codex,
  VS Code Copilot, Claude, Gemini, Antigravity, MCP, skills and hooks.
  Use when the task needs usage summaries, model/token checks, runtime event
  analysis, source scans, privacy-safe troubleshooting, or controlled deletion
  of imported analytics data. Do not use to retrieve prompt or response content.
---

# MCP Analytics Operator

Skill per usare `analytics-node` come strumento locale e privacy-first di analisi dell'utilizzo AI. Serve per interrogare sessioni, modelli, token disponibili, eventi MCP, skill e hook, senza leggere o restituire contenuti conversazionali.

## Quick Routing

Usa questa skill quando serve:

- verificare uso locale di Codex, VS Code Copilot, Claude, Gemini o Antigravity;
- produrre riepiloghi per sorgente, modello, periodo o progetto;
- controllare token nativi quando presenti nei log locali;
- analizzare eventi MCP, skill hint e hook;
- capire se una sorgente va scansionata o risincronizzata;
- cancellare dati analytics gia' importati dal DB locale.

Non usarla per:

- recuperare prompt, risposte, titoli chat o snippet;
- fare export documentali;
- stimare costi/pricing se non esiste una funzione dedicata;
- leggere token, file auth, cookie o credential store;
- analizzare codice applicativo o DB business: in quel caso passa agli skill specialistici.

## Regole Permanenti

1. Prima usa tool read-only come `analytics_status`, `analytics_summary`, `analytics_models`, `analytics_sessions` e `analytics_events`.
2. Usa `analytics_scan` solo quando i dati sono obsoleti, una sorgente e' assente o l'utente chiede aggiornamento.
3. Non chiedere mai contenuti chat: il server analytics non deve salvare o restituire prompt, risposte, titoli, snippet o raw JSON.
4. Tratta i token come dati nativi disponibili solo quando presenti nei log; non stimare token mancanti.
5. Distingui sempre uso effettivo MCP da hint: `McpHint` e' evento hook, non chiamata MCP reale.
6. Distingui `SkillHint` da uso reale di skill: la skill registra hint operativi, non conferme di esecuzione salvo evento strutturato esplicito.
7. Per cancellazioni usa sempre dry-run prima dell'esecuzione e richiedi conferma esplicita del piano.
8. Se una sorgente genera warning, segnala il warning senza inventare dati mancanti.
9. Non esporre path locali salvo richiesta esplicita di debug tramite tool che lo supportano.
10. Mantieni gli output sintetici e orientati ad azione: periodo, sorgenti, conteggi, warning, prossimi controlli.

## Workflow Base

1. `analytics_status` per verificare DB, feature, conteggi e ultimo stato noto.
2. `analytics_summary` per una vista aggregata su sorgenti, sessioni ed eventi.
3. `analytics_models` per distribuzione modelli e token nativi disponibili.
4. `analytics_events` per MCP, skill hint e hook.
5. `analytics_sessions` solo se serve elenco sessioni senza contenuti.
6. `analytics_scan` solo quando serve aggiornare il DB locale.
7. `analytics_delete_imported` solo dopo dry-run e conferma piano.

## Contratto sorgenti

Sorgenti supportate:

```text
codex
vscode_copilot
vscode_insiders_copilot
hook_log
claude_code
claude_desktop
gemini_cli
google_antigravity
```

Regole:

- usa solo nomi canonici con underscore;
- non usare alias con trattino nei tool MCP;
- se una sorgente non e' disponibile localmente, segnala assenza come warning o skip, non come errore bloccante salvo richiesta esplicita;
- non leggere file auth o token delle sorgenti.

## Cancellazione dati importati

Per cancellare dati dal DB analytics:

1. eseguire dry-run con filtri `sources` e periodo;
2. leggere `delete_plan_id` e conteggi previsti;
3. chiedere conferma utente se l'impatto e' quello atteso;
4. rieseguire con `confirm_delete: true` e `confirm_plan_id`;
5. se il piano e' cambiato, interrompere e rifare dry-run.

Non cancellare mai file originali di Codex, VS Code, Claude, Gemini o Antigravity. La cancellazione riguarda solo righe SQLite di `analytics-node`.

## Troubleshooting

- Se `analytics_status` indica DB non disponibile, verificare `ANALYTICS_DB_PATH` e permessi directory.
- Se non compaiono sessioni, eseguire `analytics_scan` sulla sorgente interessata e controllare warning.
- Se una sorgente e' assente, verificare che il client sia installato e abbia dati locali leggibili.
- Se i token sono zero, controllare se il client espone token nativi nei log: non stimare.
- Se `hook_log` non produce eventi, verificare che gli hook Sophia scrivano `events.jsonl`.
- Se una cancellazione non parte, rifare dry-run e controllare `confirm_plan_id`.
- Se una sorgente Claude/Gemini/Antigravity cambia formato, trattarla come warning parser e non forzare import non sicuro.

## Sinergie

- con `mcp-technical-analyst`: per correlare analytics con requisiti, ticket, repo o decisioni operative;
- con `mcp-code-reviewer`: per verificare che patch su `analytics-node` rispettino spec, privacy e test;
- con `mcp-docs-navigator`: per aggiornare documentazione indicizzata dopo modifiche a README o guide;
- con `mcp-memory-operator`: per salvare decisioni operative validate, non dati analytics grezzi.

## References

- [references/analytics-workflow.md](references/analytics-workflow.md)
- [references/privacy-rules.md](references/privacy-rules.md)
- [references/deletion-playbook.md](references/deletion-playbook.md)
```

### 28.3 `references/analytics-workflow.md`

```md
# Analytics workflow

## Query read-only

Sequenza consigliata:

1. `analytics_status`
2. `analytics_summary`
3. `analytics_models`
4. `analytics_events`
5. `analytics_sessions`, solo se serve dettaglio sessioni senza contenuti

## Scan

Usare `analytics_scan` quando:

- l'utente chiede dati aggiornati;
- una sorgente non compare;
- l'ultimo scan e' vecchio;
- un adapter e' stato appena aggiunto o corretto.

Preferire scan mirati:

```json
{ "sources": ["codex"], "dry_run": true }
```

Poi scan reale solo se il piano e' corretto:

```json
{ "sources": ["codex"], "dry_run": false }
```

## Sorgenti

Usare solo source canoniche:

```text
codex
vscode_copilot
vscode_insiders_copilot
hook_log
claude_code
claude_desktop
gemini_cli
google_antigravity
```

## Output atteso

Ogni risposta deve distinguere:

- dati osservati;
- warning;
- dati non disponibili;
- azione consigliata.

Non inventare token, modelli o eventi non presenti nei log locali.
```

### 28.4 `references/privacy-rules.md`

```md
# Privacy rules

## Vietato

Non richiedere, salvare, sintetizzare o restituire:

- prompt utente;
- risposte assistant;
- titoli chat;
- snippet codice;
- payload tool completi;
- raw JSON dei client;
- path completi, salvo debug esplicito;
- token, cookie, password, authorization header;
- file auth o credential store.

## Consentito

Sono ammessi solo dati analytics redatti o strutturati:

- sorgente;
- periodo;
- conteggi sessioni;
- conteggi messaggi;
- modelli strutturati;
- token nativi se presenti;
- eventi MCP strutturati;
- skill hint;
- hook event;
- warning sanitizzati;
- hash HMAC locali.

## Token

Se i token non sono presenti nei log locali:

- non stimare;
- non dedurre;
- riportare `token_available=false` o conteggi mancanti.

## Eventi

`McpHint` non e' uso MCP reale. E' un evento hook.

`SkillHint` non conferma esecuzione skill. E' un suggerimento operativo registrato dagli hook.

## Errori

Non riportare stack trace o errori parser raw. Usare codici e warning sanitizzati.
```

### 28.5 `references/deletion-playbook.md`

```md
# Deletion playbook

## Scopo

Cancellare dati importati dal DB analytics locale senza toccare i file originali dei client.

## Regola base

Ogni cancellazione reale richiede:

1. dry-run;
2. verifica conteggi;
3. conferma utente;
4. `confirm_plan_id`;
5. `confirm_delete: true`.

## Esempio dry-run per sorgente

```json
{
  "sources": ["codex"],
  "period_type": "month",
  "period_value": "2026-05",
  "dry_run": true
}
```

## Esempio cancellazione confermata

```json
{
  "confirm_plan_id": "<plan-id>",
  "confirm_delete": true,
  "dry_run": false
}
```

## Filtri

Usare filtri per sorgente e periodo:

```text
sources
date_from/date_to
period_type + period_value
delete_scope
```

`date_from/date_to` e `period_type/period_value` sono alternativi.

## Scope

```text
sessions
runtime_events
source_files
all
```

## Sicurezza

- non cancellare file sorgente;
- non cancellare settings o migrations;
- non usare filtri testuali;
- non esporre path locali;
- se il piano cambia tra dry-run e delete, annullare e rifare dry-run.
```

---

## 29. Aggiornamento documentazione e developer guide dopo la skill

Questa sezione integra il punto 27 senza modificarlo, per ridurre la diff visiva. Le attivita' seguenti sono obbligatorie nello stato branch `rework`.

### 29.1 File documentali da aggiornare

Aggiornare:

```text
README.md
analytics-node/README.md
docs/server-capability-matrix.md
docs/skill-governance-matrix.md
docs/mcp-skills-agents-development-guide.md
```

Non aggiornare:

```text
AGENTS.md
```

salvo decisione esplicita successiva.

### 29.2 README root

Aggiungere un riferimento sintetico a:

```text
analytics-mcp-server
mcp-analytics-operator
```

Contenuti minimi:

- `analytics-mcp-server` fornisce analytics locali privacy-first sull'uso AI;
- `mcp-analytics-operator` e' la skill operativa per interrogare e gestire il server;
- sorgenti supportate: Codex, VS Code Copilot, Claude, Gemini, Antigravity, hook log;
- la skill non recupera contenuti chat;
- la cancellazione riguarda solo dati importati nel DB analytics.

### 29.3 `analytics-node/README.md`

Aggiungere o aggiornare:

```text
- sezione "Skill operativa"
- riferimento a skills/mcp-analytics-operator/SKILL.md
- workflow read-only: status, summary, models, events, sessions
- workflow scan
- workflow delete imported data
- sorgenti supportate estese
- privacy constraints
- troubleshooting con riferimento alle reference della skill
```

### 29.4 `docs/server-capability-matrix.md`

Aggiornare la riga `analytics-node` includendo:

```text
skill collegata: mcp-analytics-operator
sorgenti: codex, vscode_copilot, vscode_insiders_copilot, hook_log, claude_code, claude_desktop, gemini_cli, google_antigravity
delete imported: yes
privacy-first: yes
content storage: no
pricing: post-MVP / no runtime scan
dashboard/export: no
```

Mantenere `action: no (tool separati)` se la matrice contiene questa colonna.

### 29.5 `docs/skill-governance-matrix.md`

Aggiungere una riga per `mcp-analytics-operator`.

Campi minimi:

```text
Skill: mcp-analytics-operator
MCP primario: analytics-mcp-server
Quando usarla: usage analytics locali, modelli/token, eventi MCP/skill/hook, scan sorgenti, delete imported
Quando non usarla: contenuti chat, prompt/risposte, export, pricing non implementato, analisi codice applicativo
Sidecar: mcp-technical-analyst, mcp-code-reviewer, mcp-docs-navigator, mcp-memory-operator
Privacy: no contenuti, no raw JSON, no auth/token, path redatti
Sorgenti: codex, vscode_copilot, vscode_insiders_copilot, hook_log, claude_code, claude_desktop, gemini_cli, google_antigravity
```

### 29.6 `docs/mcp-skills-agents-development-guide.md`

Aggiungere una sezione breve:

```text
Skill analytics e gestione uso AI locale
```

Contenuti minimi:

- usare `mcp-analytics-operator` quando serve interrogare `analytics-mcp-server`;
- preferire query read-only prima di scan;
- usare scan mirati per sorgente;
- usare cancellazione solo con dry-run e conferma;
- non chiedere mai contenuti chat o path locali se non per debug;
- se un adapter produce warning, correggere parser/fixture prima di fidarsi dei dati aggregati;
- correlare evidenze complesse con `mcp-technical-analyst`;
- usare `mcp-code-reviewer` per review spec-compliance delle patch analytics.

### 29.7 Done criteria documentazione aggiornata

Una milestone collegata ad analytics e skill e' chiusa solo se:

```text
skill mcp-analytics-operator aggiornata se cambiano tool o workflow
reference skill aggiornate se cambia scan/privacy/delete
README root aggiornato se cambia lista server/skill
analytics-node/README.md aggiornato se cambia comportamento tool
server-capability-matrix aggiornata se cambiano capability/source/tool
skill-governance-matrix aggiornata se cambia routing skill
developer guide aggiornata se cambia workflow operativo
AGENTS.md non modificato salvo decisione esplicita
```

---

## 30. M13 - Hardening futuro: import incrementale del log hook

La gestione attuale del log hook resta volutamente semplice: gli hook scrivono una riga JSONL in append su `dirname(ANALYTICS_DB_PATH)/hooks/events.jsonl`, mentre lo scanner legge il file attivo e deduplica gli eventi tramite `runtime_events.id` deterministico.

Questa soluzione e' accettabile come base, ma puo' diventare inefficiente se il file cresce o se `analytics_scan` viene eseguito spesso. Per ridurre I/O e tempi di scansione, prevedere una milestone di hardening dedicata all'import incrementale del log hook.

### 30.1 Obiettivo

Evitare di rileggere sempre da capo `events.jsonl`, mantenendo invariata la regola fondamentale:

```text
Gli hook fanno solo append best-effort e non aprono mai SQLite.
```

La complessita' deve restare nello scanner, non negli hook.

### 30.2 Estensione schema proposta

Aggiungere campi a `source_files` o tabella equivalente per tracciare il progresso di lettura del file hook:

```sql
ALTER TABLE source_files ADD COLUMN last_read_offset INTEGER DEFAULT 0;
ALTER TABLE source_files ADD COLUMN last_read_line_no INTEGER DEFAULT 0;
ALTER TABLE source_files ADD COLUMN last_read_mtime_ms INTEGER;
```

Questi campi sono usati solo per sorgenti append-only come `hook_log`.

### 30.3 Algoritmo scanner

Per `hook_log`:

1. risolvere `events.jsonl` attivo;
2. leggere `source_files.last_read_offset` e `last_read_line_no`;
3. se `stat.size < last_read_offset`, considerare il file ruotato/troncato e ripartire da offset `0`;
4. leggere solo le righe nuove da `last_read_offset`;
5. processare le righe distinguendo tre casi:
   - **riga completa valida**: parsare, importare evento con ID deterministico e avanzare offset/linea dopo import riuscito;
   - **riga completa ma JSON invalido**: emettere warning sanitizzato `INVALID_JSON_LINE_SKIPPED`, avanzare offset oltre quella riga e continuare con le successive;
   - **ultima riga senza newline o JSON incompleto/tronco**: non avanzare oltre l'inizio della riga, non ruotare il file e ritentare al prossimo scan;
6. se parse e validazione sono riusciti ma l'import DB fallisce, non avanzare offset/linea oltre il batch non importato;
7. aggiornare `last_read_offset` e `last_read_line_no` solo dopo import riuscito o skip esplicito di riga completa invalida;
8. mantenere la deduplica tramite `runtime_events.id` come rete di sicurezza.

Questa distinzione evita due regressioni:

- una riga JSONL completa ma invalida non blocca per sempre l'import incrementale;
- una riga tronca in fondo al file non viene persa se si completa dopo.

### 30.4 Rotazione

La rotazione resta responsabilita' dello scanner.

Regole:

```text
- gli hook non ruotano mai il file;
- lo scanner ruota solo dopo import completato delle righe valide;
- events.jsonl.1 resta diagnostico, non sorgente primaria;
- se la rotazione avviene, il nuovo events.jsonl riparte da offset 0;
- righe invalide o tronche generano warning sanitizzati, non stack trace.
```

### 30.5 Privacy e sicurezza

L'import incrementale non deve modificare le regole privacy gia' definite:

```text
no prompt
no raw stdin hook
no payload tool
no path completi
no stack trace
no raw JSON salvato
```

`last_read_offset`, `last_read_line_no` e `last_read_mtime_ms` sono metadati tecnici locali e non devono essere esposti negli output MCP ordinari. Possono comparire solo in diagnostica locale esplicita, senza path completi.

### 30.6 Test obbligatori hardening

```text
hook_log_incremental_reads_only_new_lines
hook_log_incremental_keeps_dedup_safety
hook_log_incremental_ignores_truncated_last_line
hook_log_incremental_advances_after_complete_invalid_line
hook_log_incremental_resumes_after_truncated_line_completed
hook_log_incremental_resets_on_file_truncation
hook_log_rotation_after_successful_import
hook_log_rotation_does_not_run_from_hook_helper
hook_log_incremental_does_not_expose_local_paths
```

### 30.7 Priorita'

Questa ottimizzazione non deve bloccare lo sviluppo delle funzionalita' principali. Priorita' consigliata:

```text
1. mantenere append-only semplice negli hook;
2. mantenere deduplica deterministica sempre attiva;
3. aggiungere import incrementale quando il volume di hook log o la frequenza di scan lo giustificano.
```


---

## 31. M14 - Normalizzazione source e rimozione Gemini/Antigravity

Questa sezione integra e prevale sulle sezioni precedenti quando sono in conflitto, senza modificare retroattivamente il testo gia' validato per ridurre la diff visiva.

Policy documentale per l'analisi:

- le sezioni precedenti restano storiche/non piu' fonte normativa quando citano source superate;
- da M14 in poi la fonte normativa per le source e' questa sezione;
- non modificare retroattivamente l'analisi per bonificare ogni citazione storica;
- documentazione utente, README, capability matrix e skill devono invece essere aggiornate alle source canoniche correnti.

Stato sviluppo:

- siamo ancora in fase di sviluppo sul branch `rework`;
- il DB locale esistente non e' definitivo e non richiede preservazione dati storici;
- non serve una migration legacy con mapping dati storici;
- occorre correggere direttamente struttura DB, CHECK constraint, fixture e test precedenti per allinearli alle source canoniche.

Motivazione:

- dai transcript Antigravity analizzati emergono eventi/tool, ma non token/model usage strutturati;
- l'obiettivo principale di `analytics-node` resta token/modelli/usage analytics privacy-first;
- Gemini CLI non viene targetizzato come adapter separato;
- Copilot e Claude vanno trattati come prodotti unici, distinguendo la superficie client tramite campo dedicato.

### 31.1 Source canoniche aggiornate

Da questo punto in avanti usare queste source logiche:

```ts
type AnalyticsSource =
  | 'codex'
  | 'copilot'
  | 'claude'
  | 'hook_log';
```

Non usare piu' come source canoniche:

```text
vscode_copilot
vscode_insiders_copilot
claude_code
claude_desktop
gemini_cli
google_antigravity
antigravity
```

Nota: i nomi storici presenti nelle sezioni precedenti restano documentazione pregressa, ma le implementazioni successive devono convergere sulle source canoniche di questa sezione.

### 31.2 `client_surface`

Per distinguere IDE, CLI o variante client usare un campo separato:

```ts
type ClientSurface =
  | 'cli'
  | 'desktop'
  | 'vscode'
  | 'vscode_insiders'
  | 'code'
  | 'unknown';
```

Mapping iniziale:

| Prodotto | `source` | `client_surface` |
|---|---|---|
| Codex CLI | `codex` | `cli` |
| Codex Desktop | `codex` | `desktop` |
| Codex in VS Code, se distinguibile | `codex` | `vscode` |
| GitHub Copilot in VS Code | `copilot` | `vscode` |
| GitHub Copilot in VS Code Insiders | `copilot` | `vscode_insiders` |
| Claude Code | `claude` | `code` |
| Claude Desktop, se fixture reale disponibile | `claude` | `desktop` |
| Hook log Sophia | `hook_log` | non applicabile / `NULL` |

### 31.3 Schema e test da correggere in fase sviluppo

Poiche' il DB non e' ancora finale, M14 non deve implementare una migration legacy dati storici. Deve invece aggiornare lo schema corrente e i test gia' presenti:

- cambiare i `CHECK` su `source_files.source`, `sessions.source`, `runtime_events.source` usando solo `codex`, `copilot`, `claude`, `hook_log`;
- aggiornare `scan_runs.sources_json` e ogni validazione server-side per usare solo source canoniche;
- aggiornare fixture/test precedenti che usano `vscode_copilot`, `vscode_insiders_copilot`, `claude_code`, `claude_desktop`, `gemini_cli`, `google_antigravity`, `antigravity`;
- rigenerare eventuale DB di test da zero tramite migrations aggiornate;
- non prevedere mapping dati storici nel codice applicativo.

Aggiungere a `sessions`:

```sql
client_surface TEXT CHECK (client_surface IN ('cli', 'desktop', 'vscode', 'vscode_insiders', 'code', 'unknown'))
```

Per `runtime_events` non duplicare `client_surface` se l'evento ha `session_id` e il valore e' derivabile da `sessions`. Per eventi senza sessione, come `hook_log`, lasciare `client_surface` non applicabile.

Nota per un futuro post-release: quando esisteranno DB reali da preservare, una successiva migration dovra' essere non distruttiva e, se necessario, rebuildare le tabelle SQLite per aggiornare i `CHECK`. Questa attivita' non fa parte di M14.

Nota operativa sviluppo `rework`:

- dopo modifiche dirette a migrations/schema durante M14, eliminare e ricreare solo il DB analytics dev/test;
- in alternativa usare un `ANALYTICS_DB_PATH` temporaneo dedicato agli smoke test;
- non implementare reset automatici su DB utente;
- documentare nel README dev come rigenerare il DB locale di sviluppo quando cambiano i `CHECK` delle source.

### 31.4 Rimozione Gemini e Antigravity

Rimuovere da nuove implementazioni, schema, tool contract, delete filter, skill e documentazione:

```text
gemini_cli
google_antigravity
antigravity
Antigravity IDE/CLI
```

Nota backlog consentita:

```text
Antigravity escluso: i transcript osservati espongono eventi/tool, ma non token/model usage strutturato. Rivalutabile solo se versioni future loggano token/model usage in modo strutturato e privacy-safe.
```

### 31.5 File interessati

```text
analytics-node/src/sources.ts
analytics-node/src/db.ts
analytics-node/src/migrations.ts
analytics-node/src/adapters/copilot.ts
analytics-node/src/adapters/claude.ts
analytics-node/src/scanner.ts
analytics-node/src/tools/summary.ts
analytics-node/src/tools/models.ts
analytics-node/src/tools/sessions.ts
analytics-node/src/tools/events.ts
analytics-node/README.md
skills/mcp-analytics-operator/SKILL.md
skills/mcp-analytics-operator/references/analytics-workflow.md
skills/mcp-analytics-operator/references/deletion-playbook.md
docs/server-capability-matrix.md
docs/skill-governance-matrix.md
docs/mcp-skills-agents-development-guide.md
```

Non creare un nuovo `analytics-node/src/tools/scan.ts` se la logica scan corrente resta in `analytics-node/src/scanner.ts`. Eventuali refactor verso tool separati sono fuori scope M14.

### 31.6 Test obbligatori M14

```text
source_enum_uses_codex_copilot_claude_hook_log
no_vscode_copilot_source_registered
no_vscode_insiders_copilot_source_registered
no_claude_code_or_claude_desktop_source_registered
no_gemini_or_antigravity_source_registered
copilot_vscode_maps_to_source_copilot_surface_vscode
copilot_insiders_maps_to_source_copilot_surface_vscode_insiders
claude_code_maps_to_source_claude_surface_code
schema_check_constraints_use_only_canonical_sources
legacy_source_fixtures_are_updated_or_rejected
no_legacy_data_migration_required_in_rework
```

---

## 32. M15 - Codex fixture reale: token, modelli, MCP e subagent

Questa milestone formalizza quanto osservato dal file Codex reale:

```text
C:\Users\<redacted>\.codex\sessions\<yyyy>\<mm>\<dd>\<redacted>.jsonl
```

Il file non deve essere committato come fixture reale. Va usato solo per derivare fixture sintetiche redatte.

I path e gli identificativi reali devono restare redatti in documentazione, test e fixture committate.

### 32.1 Token Codex

Codex espone token nativi in eventi strutturati:

```text
event_msg.payload.type = token_count
payload.info.last_token_usage.input_tokens
payload.info.last_token_usage.cached_input_tokens
payload.info.last_token_usage.output_tokens
payload.info.last_token_usage.reasoning_output_tokens
payload.info.last_token_usage.total_tokens
payload.info.total_token_usage.input_tokens
payload.info.total_token_usage.cached_input_tokens
payload.info.total_token_usage.output_tokens
payload.info.total_token_usage.reasoning_output_tokens
payload.info.total_token_usage.total_tokens
```

Regole import e anti doppio conteggio:

- importare token solo da eventi `token_count` strutturati;
- non stimare token mancanti;
- usare `last_token_usage` come delta del turno/record importabile in `message_metrics` o nel record turno equivalente;
- non sommare mai piu' eventi `total_token_usage`, perche' sono cumulativi e gonfierebbero i report;
- usare `total_token_usage` solo come ultimo totale sessione osservato o diagnostica aggregata allowlisted, non come sorgente sommabile per `message_metrics`;
- se si decide di salvare l'ultimo totale sessione, usare colonne dedicate o tabella aggregata distinta da `message_metrics`, con semantica esplicita `session_total_*`;
- mappare `last_token_usage.cached_input_tokens` su `cache_read_tokens` se il campo indica cache read;
- conservare `reasoning_output_tokens` solo se viene aggiunta una colonna esplicita o un aggregato allowlisted;
- se non viene aggiunta colonna dedicata, non salvare `reasoning_output_tokens` nel MVP della milestone.

Test specifico obbligatorio:

```text
codex_does_not_double_count_cumulative_total_token_usage
```

### 32.2 Modelli Codex

Il file osservato espone modelli strutturati, ad esempio:

```text
gpt-5.4
gpt-5.4-mini
```

Regole:

- salvare `model_primary` a livello sessione se strutturato e sanitizzato;
- salvare `message_metrics.model` solo quando il log espone il modello per messaggio/turno in modo certo;
- massimo 160 caratteri, niente newline, niente JSON, niente path.

### 32.3 MCP Codex

Codex espone chiamate MCP tramite nomi funzione in forma:

```text
mcp__<server>__<tool>
```

Esempi osservati:

```text
mcp__git_mcp_server__git_diff
mcp__git_mcp_server__git_query
mcp__projectfs_mcp_server__read_file
mcp__docs_mcp_server__docs_navigation
mcp__linter_mcp_server__lint_code
```

Mapping:

```text
mcp__git_mcp_server__git_diff
=> mcp_server_name = git-mcp-server
=> tool_name = git_diff
=> event_name = git-mcp-server.git_diff
```

La normalizzazione deve passare da allowlist, per evitare mapping arbitrari:

```ts
normalizeCodexMcpServerName("git_mcp_server") === "git-mcp-server"
```

Importare solo se:

```text
server normalizzato in allowlist MCP
tool_name presente
evento strutturato come function_call/custom_tool_call compatibile
```

### 32.4 Subagent / collab agent Codex

Il file Codex osservato contiene eventi collaborativi/subagent:

```text
collab_agent_spawn_end
collab_close_end
new_thread_id
sender_thread_id
receiver_thread_id
new_agent_nickname
new_agent_role
```

Decisione:

- tracciare questi dati solo come lifecycle/eventi agentici se viene attivata la milestone runtime events estesa;
- non creare automaticamente sessioni subagent separate dal solo lifecycle event;
- non attribuire token al subagent se `token_count` non contiene `thread_id`, `agent_id`, `subagent_id` o correlazione strutturata certa.

Regola esplicita:

```text
Per Codex, `session_kind` serve a classificare sessioni o lifecycle event, non ad attribuire automaticamente token ai subagent. Nel JSONL osservato i token sono esposti tramite eventi `token_count` con usage aggregata, senza correlazione strutturata certa al subagent. Pertanto i token vengono conteggiati sulla sessione/turno principale. Eventuali subagent vengono tracciati come lifecycle, senza token dedicati, salvo futuri log con `thread_id`/`agent_id` nei token_count.
```

### 32.5 `session_kind`

Introdurre solo se serve distinguere sessioni realmente separate o subagent Claude/Codex:

```ts
type SessionKind =
  | 'main'
  | 'subagent'
  | 'task'
  | 'unknown';
```

Schema:

```sql
ALTER TABLE sessions ADD COLUMN session_kind TEXT
  CHECK (session_kind IN ('main', 'subagent', 'task', 'unknown'));
```

Default:

```text
main per sessioni principali
subagent solo per file/sessioni subagent reali
task solo se il formato espone task separati
unknown se non classificabile
```

### 32.6 Test obbligatori M15

```text
codex_imports_token_count_events
codex_imports_last_token_usage_as_turn_delta
codex_stores_total_token_usage_only_as_session_total_if_enabled
codex_does_not_double_count_cumulative_total_token_usage
codex_imports_model_from_structured_fields
codex_imports_mcp_from_function_call_names
codex_normalizes_mcp_server_underscore_to_dash_allowlisted
codex_rejects_unknown_mcp_server_mapping
codex_detects_collab_agent_spawn_without_token_attribution
codex_does_not_create_subagent_session_from_lifecycle_only
codex_does_not_save_prompt_response_or_raw_json
```

---

## 33. M16 - Copilot source unica e token da VS Code chatSessions

Questa milestone normalizza Copilot come source unica e formalizza quanto osservato dal file reale:

```text
%APPDATA%\Code\User\workspaceStorage\<redacted>\chatSessions\<redacted>.jsonl
```

Il file non deve essere committato come fixture reale. Va usato solo per derivare fixture sintetiche redatte.

I path e gli identificativi reali devono restare redatti in documentazione, test e fixture committate.

### 33.1 Source e adapter

Usare:

```text
source = copilot
client_surface = vscode | vscode_insiders | unknown
```

I resolver fisici restano separati:

```ts
resolveVsCodeCopilotRoots(): string[]
resolveVsCodeInsidersCopilotRoots(): string[]
```

ma entrambi producono record con `source='copilot'`.

Consiglio file adapter:

```text
src/adapters/copilot.ts
```

con funzioni interne:

```ts
scanVsCodeCopilot()
scanVsCodeInsidersCopilot()
```

### 33.2 Token Copilot

Il file osservato espone token strutturati:

```text
v.requests[].result.metadata.promptTokens
v.requests[].result.metadata.outputTokens
v.requests[].completionTokens
```

Regole mapping:

```text
promptTokens -> input_tokens
outputTokens o completionTokens -> output_tokens
```

Se entrambi `outputTokens` e `completionTokens` sono presenti, definire priorita' esplicita:

```text
preferire result.metadata.outputTokens se presente;
usare completionTokens come fallback;
non sommare i due campi per evitare doppio conteggio.
```

### 33.3 Modello Copilot

Il file osservato espone:

```text
modelId = copilot/gpt-4.1
```

Regole:

- salvare modello se campo strutturato;
- applicare sanitizzazione modello;
- non inferire modello da testo libero.

### 33.4 Tool call Copilot

La struttura contiene:

```text
v.requests[].result.metadata.toolCallRounds
```

Nel campione osservato `toolCalls` e' vuoto.

Regole:

- se `toolCallRounds[].toolCalls` e' vuoto, non creare `runtime_events`;
- se contiene tool call, importare solo MCP strutturati riconducibili alla allowlist;
- non importare tool interni generici come MCP.

### 33.5 Test obbligatori M16

```text
copilot_source_unified_for_vscode_and_insiders
copilot_vscode_surface_vscode
copilot_insiders_surface_vscode_insiders
copilot_imports_prompt_and_output_tokens
copilot_prefers_output_tokens_over_completion_tokens
copilot_imports_model_id_when_structured
copilot_tool_call_rounds_empty_creates_no_events
copilot_does_not_read_auth_or_token_files
copilot_does_not_save_prompt_response_or_raw_json
```

---

## 34. M17 - Claude source unica e supporto subagent

Questa milestone sostituisce le precedenti distinzioni `claude_code` / `claude_desktop` con source unica `claude`.

### 34.1 Source e surface

Usare:

```text
source = claude
client_surface = code | desktop | unknown
```

Per Claude Code Windows, path osservato:

```text
%USERPROFILE%\.claude\projects\<project-slug>\
```

Candidate roots:

```text
%USERPROFILE%\.claude\projects\**\*.jsonl
%USERPROFILE%\.claude\projects\**\subagents\*.jsonl
~/.claude/projects/**/*.jsonl
~/.claude/projects/**/subagents/*.jsonl
```

Claude Desktop resta `client_surface=desktop` solo quando sara' disponibile fixture reale.

### 34.2 File da leggere e ignorare

Fonti primarie Claude Code:

```text
<project-slug>/<session-id>.jsonl
<project-slug>/<session-id>/subagents/*.jsonl
```

Ignorare in prima implementazione:

```text
tool-results/*.txt
memory/MEMORY.md
*.meta.json, salvo uso futuro per classificazione subagent
```

### 34.3 Token e modelli Claude

Claude Code espone token/modelli strutturati in messaggi assistant:

```text
message.model
message.usage.input_tokens
message.usage.cache_creation_input_tokens
message.usage.cache_read_input_tokens
message.usage.output_tokens
```

Mapping:

```text
input_tokens = usage.input_tokens
cache_write_tokens = usage.cache_creation_input_tokens
cache_read_tokens = usage.cache_read_input_tokens
output_tokens = usage.output_tokens
token_available = 1
```

Non salvare campi diagnostici o metadati non necessari, salvo allowlist futura.

### 34.4 MCP Claude

Fonti MCP strutturate ammesse:

```text
attributionMcpServer
attributionMcpTool
```

oppure tool name in forma:

```text
mcp__<server>__<tool>
```

Importare solo se il server e' allowlisted. Ignorare tool interni Claude come:

```text
Read
Bash
Glob
Grep
Edit
PowerShell
Agent
ToolSearch
Write
ExitPlanMode
```

### 34.5 Subagents Claude

Per subagents:

```text
source = claude
client_surface = code
session_kind = subagent
```

Non creare una source separata.

### 34.6 Test obbligatori M17

```text
claude_source_unified
claude_code_surface_from_projects_path
claude_main_jsonl_imports_sessions_without_content
claude_subagents_import_as_subagent_sessions_without_content
claude_imports_native_token_usage
claude_imports_model_when_structured
claude_imports_mcp_events_from_attribution_fields
claude_imports_mcp_events_from_mcp_tool_use_name_only
claude_ignores_internal_tools_read_bash_edit_grep_glob
claude_ignores_tool_results_txt
claude_ignores_memory_md
claude_does_not_save_cwd_gitbranch_slug_lastprompt_aititle
```

---

## 35. M18 - Eventi runtime canonici e observations anti-duplicazione

Questa milestone evita duplicazioni tra eventi letti dai transcript chat e eventi/hint letti dal log hook.

### 35.1 Problema

Lo stesso fatto operativo puo' comparire in piu' fonti:

```text
Codex transcript: mcp__git_mcp_server__git_diff
hook log: McpHint / PreToolUse / routing hint
Claude transcript: attributionMcpServer / attributionMcpTool
Copilot transcript: toolCallRounds
```

Non bisogna contare due volte una chiamata MCP reale solo perche' esistono sia transcript sia hook/hint.

### 35.2 Modello dati

`runtime_events` rappresenta l'evento canonico normalizzato da contare.

Aggiungere tabella:

```sql
CREATE TABLE IF NOT EXISTS runtime_event_observations (
  id TEXT PRIMARY KEY,
  runtime_event_id TEXT NOT NULL,
  observed_source TEXT NOT NULL CHECK (observed_source IN ('codex', 'copilot', 'claude', 'hook_log')),
  source_file_id TEXT NOT NULL,
  session_id TEXT,
  source_line_no INTEGER,
  event_seq INTEGER,
  observation_kind TEXT NOT NULL CHECK (
    observation_kind IN ('chat_structured', 'hook_log', 'hook_hint', 'skill_hint')
  ),
  observed_at TEXT NOT NULL,
  FOREIGN KEY(runtime_event_id) REFERENCES runtime_events(id) ON DELETE CASCADE,
  FOREIGN KEY(source_file_id) REFERENCES source_files(id) ON DELETE CASCADE
);
```

Regole observation:

- `source_file_id` deve sempre puntare a una riga `source_files` esistente;
- le observation vengono cancellate a cascata quando viene cancellato il runtime event canonico o il file sorgente;
- un `SkillHint` osservato dagli hook usa `event_type='skill'` sul runtime event canonico e `observation_kind='skill_hint'`;
- `skill_hint` non equivale a skill usata, ma solo a suggerimento operativo osservato.

Regola:

```text
runtime_events = cosa e' successo
runtime_event_observations = dove e' stato osservato
```

### 35.3 Event type estesi

Per supportare tool interni e lifecycle agent, estendere post-MVP:

```ts
type RuntimeEventType =
  | 'mcp'
  | 'skill'
  | 'hook'
  | 'agent_tool'
  | 'agent_lifecycle';
```

Definizioni:

```text
mcp             chiamata MCP reale osservata in modo strutturato
skill           skill hint o futura skill usage strutturata
hook            hook invocation o hook hint
agent_tool      tool interno del client AI, non MCP
agent_lifecycle spawn/close subagent, task lifecycle, sidechain/collab lifecycle
```

MCP reale non deve includere `McpHint`.

### 35.4 Regole di conteggio

Report separati:

```json
{
  "mcp_calls": {},
  "hook_events": {},
  "skill_hints": {},
  "agent_tools": {},
  "agent_lifecycle": {}
}
```

Non sommare mai questi gruppi in un unico totale "tool usage".

### 35.5 Fingerprint canonici

MCP reale:

```text
event_type=mcp
session_id se disponibile
mcp_server_name
tool_name
occurred_at bucket se affidabile
args_hash se disponibile
```

Hook hint:

```text
event_type=hook
hook_name
hook_event
occurred_at
related_mcp_server se presente
```

Agent lifecycle:

```text
event_type=agent_lifecycle
source
session_id
event_name
event_seq o source_line_no
```

### 35.6 Tool contract update

Quando M18 estende `RuntimeEventType`, vanno aggiornati anche i tool MCP e le query, non solo lo schema DB.

Aggiornamenti obbligatori:

- `analytics_events.event_type` deve accettare `mcp`, `skill`, `hook`, `agent_tool`, `agent_lifecycle`;
- `analytics_summary.group_by` e gli output aggregati devono separare `mcp_calls`, `hook_events`, `skill_hints`, `agent_tools`, `agent_lifecycle`;
- `analytics_summary` non deve sommare categorie diverse in un unico totale generico `tool_usage`;
- eventuali delete/report su `runtime_events` devono filtrare anche i nuovi `event_type`;
- i test schema strict devono essere aggiornati per accettare i nuovi valori e rifiutare valori sconosciuti.

Test specifici obbligatori:

```text
events_schema_accepts_agent_tool_and_agent_lifecycle
summary_separates_extended_event_types
events_rejects_unknown_extended_event_type
```

### 35.6 Test obbligatori M18

```text
runtime_event_observations_created_for_chat_structured_mcp
runtime_event_observations_created_for_hook_hint
runtime_event_observations_created_for_skill_hint
runtime_event_observations_reject_unknown_source_file
runtime_event_observations_dedup_cross_source
skill_hint_observation_not_counted_as_skill_usage
hook_hint_not_counted_as_mcp_call
mcp_call_count_uses_runtime_events_not_observations
agent_tool_not_counted_as_mcp_call
agent_lifecycle_not_counted_as_mcp_call
events_schema_accepts_agent_tool_and_agent_lifecycle
summary_separates_extended_event_types
events_rejects_unknown_extended_event_type
codex_does_not_duplicate_mcp_with_hook_hint
```

---

## 36. M19 - Delete filters, skill e documentazione allineati alle source canoniche

Questa milestone riallinea cancellazione, skill e documentazione alla nuova tassonomia source.

Policy documentale M19:

- M19 non modifica retroattivamente l'analisi storica delle sezioni precedenti;
- M19 aggiorna solo documentazione operativa e utente, skill e riferimenti collegati allo stato implementato;
- README, capability matrix, skill governance matrix e developer guide devono presentare come supportate solo le source canoniche correnti;
- eventuali riferimenti storici nell'analisi restano ammessi se marcati come sezioni storiche/non normative dalla policy M14.

### 36.1 Delete sources

Usare:

```json
["codex", "copilot", "claude", "hook_log"]
```

Non usare piu':

```text
vscode_copilot
vscode_insiders_copilot
claude_code
claude_desktop
gemini_cli
google_antigravity
antigravity
```

### 36.2 Filtri opzionali delete

Aggiungere:

```json
{
  "client_surfaces": {
    "type": "array",
    "items": {
      "type": "string",
      "enum": ["cli", "desktop", "vscode", "vscode_insiders", "code", "unknown"]
    }
  },
  "session_kinds": {
    "type": "array",
    "items": {
      "type": "string",
      "enum": ["main", "subagent", "task", "unknown"]
    }
  }
}
```

Esempi:

```json
{
  "sources": ["copilot"],
  "client_surfaces": ["vscode_insiders"],
  "period_type": "month",
  "period_value": "2026-05",
  "dry_run": true
}
```

```json
{
  "sources": ["claude"],
  "client_surfaces": ["code"],
  "session_kinds": ["subagent"],
  "dry_run": true
}
```

### 36.3 Skill `mcp-analytics-operator`

Aggiornare sorgenti supportate:

```text
codex
copilot
claude
hook_log
```

Aggiungere regola:

```text
Usare `client_surface` per distinguere CLI, Desktop, VS Code, VS Code Insiders e Claude Code quando disponibile.
```

Rimuovere dalla skill:

```text
vscode_copilot
vscode_insiders_copilot
claude_code
claude_desktop
gemini_cli
google_antigravity
antigravity
```

### 36.4 Documentazione

Aggiornare la documentazione operativa, non la parte storica dell'analisi:

```text
README.md
analytics-node/README.md
docs/server-capability-matrix.md
docs/skill-governance-matrix.md
docs/mcp-skills-agents-development-guide.md
```

Sostituire in questi documenti le liste source precedenti con:

```text
codex, copilot, claude, hook_log
```

e documentare:

```text
client_surface per distinguere varianti client
session_kind per distinguere main/subagent/task quando disponibile
Antigravity/Gemini esclusi finche' non espongono token/model usage strutturato
```

La verifica documentale deve ignorare citazioni legacy presenti nell'analisi tecnica storica, ma non deve ignorarle in README, capability matrix, skill governance matrix, developer guide e skill operative.

### 36.5 Test obbligatori M19

```text
delete_sources_use_canonical_source_enum
delete_supports_client_surfaces_filter
delete_supports_session_kinds_filter
delete_rejects_removed_sources
skill_lists_only_canonical_sources
docs_do_not_reference_removed_sources_as_supported_outside_historical_analysis
capability_matrix_lists_canonical_sources
```

---

## 37. M20 - Aggiornamento prompt Codex e criteri di review per le milestone estese

Questa milestone aggiorna i prompt operativi e le review checklist dopo l'introduzione delle source canoniche e degli eventi osservati.

### 37.1 Prompt M14

```text
Implementa solo M14: normalizza le source analytics in codex, copilot, claude, hook_log; aggiungi client_surface; rimuovi Gemini/Antigravity e source legacy. Siamo in fase sviluppo: correggi schema, CHECK constraint, fixture e test correnti, senza migration legacy dati storici. Non modificare adapter oltre il mapping necessario. Esegui test M14 obbligatori.
```

### 37.2 Prompt M15

```text
Implementa solo M15: aggiorna Codex adapter usando fixture sintetiche derivate dal JSONL reale. Importa token_count usando last_token_usage come delta e total_token_usage solo come ultimo totale sessione/diagnostica non sommabile. Importa modelli strutturati e MCP mcp__server__tool allowlisted. Rileva lifecycle subagent senza attribuire token ai subagent. Esegui test M15 obbligatori.
```

### 37.3 Prompt M16

```text
Implementa solo M16: unifica Copilot come source copilot con client_surface vscode/vscode_insiders. Importa promptTokens/outputTokens/completionTokens senza doppio conteggio e gestisci toolCallRounds vuoti. Esegui test M16 obbligatori.
```

### 37.4 Prompt M17

```text
Implementa solo M17: aggiungi Claude come source unica con client_surface code/desktop/unknown e session_kind main/subagent/unknown. Importa token e modelli strutturati, MCP allowlisted, ignora tool interni e file rischiosi. Esegui test M17 obbligatori.
```

### 37.5 Prompt M18

```text
Implementa solo M18: aggiungi runtime_event_observations con FK a runtime_events e source_files e deduplica cross-source. Separa MCP reali, hook/hint, skill hint, agent_tool e agent_lifecycle. Aggiorna tool contract, schema MCP, summary/query/delete per i nuovi event_type. Non sommare categorie diverse nei report. Esegui test M18 obbligatori.
```

### 37.6 Prompt M19

```text
Implementa solo M19: riallinea delete filters, skill mcp-analytics-operator e documentazione operativa alle source canoniche codex/copilot/claude/hook_log, client_surface e session_kind. Non modificare retroattivamente l'analisi storica; aggiorna README, capability matrix, skill governance matrix, developer guide e skill operative. Rimuovi riferimenti a Gemini/Antigravity come source supportate nella documentazione operativa. Esegui test M19 obbligatori.
```

### 37.7 Review checklist per M14-M19

```text
no source legacy registrate
nessun path reale utente/macchina in documentazione o fixture committate
nessun contenuto chat salvato
nessun raw JSON salvato
nessuna stima token
token solo da campi nativi strutturati
MCP reale solo da tool call strutturate allowlisted
hook hint non conta come MCP
skill hint non conta come skill usata
agent_tool non conta come MCP
agent_lifecycle non conta come MCP
delete non cancella file sorgente
output MCP sempre content + structuredContent
schema MCP strict con arrays items
```
