# 06 — Analisi del branch candidato `origin/hooks-routing-llm`

Fonte evidenze: `hooks-routing-llm-diff.raw.json` (subagente D) + verifica di sintesi del main agent. SHA branch `b1f3a31a`, merge-base `5079ce17`, 25 file, +6066/−107, 5 commit (WP1–WP5).

## Diff semantico — cosa il branch È davvero

**FACT centrale**: il nome del branch è fuorviante. Il branch NON introduce un routing LLM. Introduce lo *scaffolding deterministico* per un futuro valutatore semantico opzionale, mantenendo il comportamento legacy byte-identico (verificato da test di equivalenza dedicati, `tests/routing-hooks.test.mjs:~63-93`).

Componenti aggiunti:

| Componente | Stato | Evidenza |
| --- | --- | --- |
| Metadata semantici per skill (`routing-semantic-overrides.json`, sorgente git-tracked) | attivo (merge nel catalogo) | `build-routing-catalog.mjs:434-455` |
| Catalogo MCP (`build-mcp-catalog.mjs` → `mcp-catalog.json` generato + manifest availability `~/.mcp-servers/mcp-availability.json`) | attivo al build/install | `state-manager.js`, `install-user-runtime.js` |
| Ambiguity gate deterministico (`routing-ambiguity-gate.mjs`) | implementato, calcolato a ogni prompt, ma **`enabled:false` di default** → sempre `{ambiguous:false, reasons:['disabled']}` in produzione | `routing-ambiguity-gate.mjs:16-18,130-131` |
| Contract del valutatore (`routing-evaluator-contract.mjs`: allowlist + parser strict) | implementato, puro, **nessuna chiamata a provider in tutto il diff** (grep fetch/http/spawn/ollama: zero match nei file nuovi) | header `:1-4` |
| Policy resolver (`routing-policy-resolver.mjs`: modes off/shadow/observe/boost-only/hybrid) | implementato e testato ma **mai importato dal hook live** — raggiungibile solo dai test | `sophia-user-prompt-submit.mjs` (nessun import) |
| Candidate evidence (`diagnostics.candidateScores` con matchedSignals) | attivo | `routing-engine.mjs:178-273` |

## Architettura effettiva (pipeline verificata nel codice)

```text
prompt → normalizzazione [SÌ, invariata] → catalogo skill+semantic overrides [SÌ]
→ segnali deterministici con evidence trail [SÌ] → scoring/ranking [SÌ, semantica invariata]
→ anti-pattern [SÌ, invariato] → candidate set skill+MCP [SÌ]
→ ambiguity gate [SÌ ma disabilitato di default]
→ valutatore LLM [NO — non esiste client/transport]
→ merge/override/boost [SÌ come modulo, NON cablato nel hook]
→ hint finale [SÌ, max 2, output equivalente a master]
→ telemetry [PARZIALE: evento invariato da master; ambiguity/candidateScores calcolati ma MAI loggati]
```

## Risposte alle 20 domande obbligatorie (§9.2)

1. **Routing deterministico fonte primaria?** SÌ — è l'unica fonte; il resto è scaffolding spento (FACT).
2. **LLM solo su ambigui?** Per design sì (gate → allowlist), ma non verificabile a runtime: l'LLM non esiste ancora (FACT).
3. **Advisory/boost-only/sostitutivo?** Il policy resolver implementa boost-only bounded con `minimumRelevance` e protezione lock/explicit/hard-excluded; i mode off/shadow/observe restituiscono il baseline (FACT, `routing-policy-resolver.mjs:155-236`).
4. **Fallback deterministico completo?** SÌ, fail-closed su ogni ramo: evaluation nulla/invalida/low-confidence/uncertain → baseline con `fallbackReason`; nessun path di throw (FACT).
5. **Funziona offline/senza provider?** SÌ — nessuna dipendenza di rete nel diff (FACT).
6. **Fail-closed coerente?** SÌ nei moduli; coerente anche col fallback catalogo-vuoto pre-esistente (FACT).
7. **Ambiguity gate riduce le chiamate?** Non misurabile: gate disabilitato e nessuna chiamata esiste. Il design lo prevede (HYPOTHESIS da validare in shadow mode).
8. **Soglia ambiguità verificabile?** Sì come config validata e testata (`routing-hooks.test.mjs:~95-197`), no come dato empirico: nessuna telemetria la osserva (FACT).
9. **Decisioni riproducibili?** Sì: tutto deterministico, `catalogFingerprint` sha256 stabile (FACT).
10. **Rischio oscillazione tra skill?** Basso allo stato attuale (nessun evaluator); il resolver protegge locks e vieta displacement (FACT). Con evaluator reale: da verificare via eval di stabilità (HYPOTHESIS).
11. **Router più costoso del task?** Rischio nullo oggi (zero chiamate). Il design doc prevede Ministral 3B locale con timeout 1500ms — costo/latenza andrà misurato (HYPOTHESIS).
12. **LLM riceve più contesto del necessario?** Il contract limita a allowlist di id + reason codes (maxSkills 2, maxMcps 1, maxResponseChars 8000); il test asserta assenza di tool/command/path/env/secret (`tests/routing-evaluator-contract.test.mjs:38`). La minimizzazione del *prompt* è però solo nel design doc, non nel codice (FACT + gap).
13. **Dati sensibili al provider?** Oggi nulla viene inviato. Il rischio futuro dipende dall'implementazione della redazione del prompt, non ancora scritta (FACT).
14. **Caching?** NO nel codice (solo nel design doc §cache) (FACT).
15. **Timeout?** NO nel codice (design doc: providerTimeoutMs 1500) (FACT).
16. **Circuit breaker/fallback?** Fallback sì (v. 4); circuit breaker NO nel codice (FACT).
17. **Modalità light evita la valutazione costosa?** N/A: nessuna valutazione costosa esiste; `!quick` invariato (FACT).
18. **Shadow mode osservabile ma non invasiva?** Non invasiva SÌ (ritorna baseline); osservabile **NO**: né `routing.ambiguity` né i campi evaluator entrano nel log analytics — la shadow mode oggi sarebbe cieca (FACT, gap principale).
19. **Default configuration sicura?** SÌ: `enabled:false` + `mode:'off'` + resolver non cablato (FACT).
20. **Eval coprono provider failure/timeout/output invalido?** Output invalido sì, ma solo su oggetti JS costruiti a mano; provider failure/timeout/circuit breaker NO (non esiste codice da testare) (FACT).

## Costi, privacy, osservabilità, rollback

- **Costi**: zero costi runtime aggiunti oggi (una valutazione di ambiguità pure-function per prompt, trascurabile). Costi futuri interamente dipendenti dal client non ancora scritto.
- **Privacy**: nessun dato lascia la macchina; il manifest availability scrive solo stato di configurazione server in `~/.mcp-servers/mcp-availability.json` (nuovo stato persistente, non nel repo).
- **Osservabilità**: regressione *relativa alle aspettative*: il branch calcola candidateScores e ambiguity ma non li logga; l'evento analytics è invariato da master (`sophia-user-prompt-submit.mjs:1072-1080`). Impossibile fare tuning della soglia o validare il gate senza questo wiring.
- **Rollback**: banale — feature off di default; revert dei 5 commit non tocca stato persistente del repo; unico residuo è il file `mcp-availability.json` in home (innocuo).
- **Test**: 2 nuove suite + ~170 righe su routing-hooks; equivalenza legacy assertata; CI-gated via `test:routing:unit`.

## Merge readiness per area

| Area | Verdetto | Motivazione |
| --- | --- | --- |
| Backward compatibility | **READY** | Equivalenza output assertata dai test; alias `routePrompt` conservato |
| Comportamento con config assente | **READY** | Default off ovunque; fallback catalogo invariato |
| Comportamento con catalogo invalido | **READY** | Validatori nuovi falliscono il build, runtime mantiene fallback pre-esistente |
| Comportamento offline | **READY** | Nessuna dipendenza di rete |
| Determinismo | **READY** | Pure functions + fingerprint |
| Costi | **READY** | Zero costi aggiunti allo stato attuale |
| Privacy | **READY_WITH_GUARDRAILS** | OK oggi; la minimizzazione del prompt per il futuro evaluator è solo design — da implementare PRIMA del client |
| Telemetry | **NEEDS_TESTS** (in realtà: needs wiring) | ambiguity/candidateScores mai loggati → shadow mode inservibile; da cablare prima di abilitare il gate |
| Cross-client | **READY_WITH_GUARDRAILS** | Moduli coperti da smoke portable (`smoke-codex-hooks.js` PROMPT_ROUTING_MODULES); nessuna verifica live su Cursor/Antigravity |
| Test | **READY_WITH_GUARDRAILS** | Buona copertura unitaria; mancano (per forza di cose) test di transport/timeout/circuit-breaker |
| Documentazione | **READY_WITH_GUARDRAILS** | Design doc esteso ma in `docs/analisi-tecniche/TODO/`; manca doc operativa breve su flag/fasi |
| Rollback / feature flag | **READY** | Doppio flag off-by-default; resolver non cablato |
| Default configuration | **READY** | Sicura per costruzione |
| `test-affected` policy change (package.json non più transversal) | **NEEDS_TESTS** / attenzione | Cambio di policy CI *non correlato* al tema del branch, infilato in WP: può mascherare regressioni da edit a package.json; da valutare separatamente (INFERENCE) |

## Problemi risolti e introdotti

**Risolti/migliorati (RESOLVED_BY_BRANCH):** evidenza per-candidato (matchedSignals) prima assente → osservabilità del *perché* di un ranking; metadata semantici versionati e validati; catalogo MCP con availability reale; fingerprint stabile del catalogo; copertura test del routing aumentata.

**Introdotti (INTRODUCED_BY_BRANCH):**
1. Codice irraggiungibile in produzione (`routing-policy-resolver.mjs` mai importato) — accettabile come scaffolding SE dichiarato; oggi lo dichiara solo il design doc (FACT).
2. Telemetria promessa ma non cablata (v. sopra) (FACT).
3. Nuovo stato persistente in home (`mcp-availability.json`) scritto a ogni save — nessuna migrazione versionata dichiarata per questo file (FACT; il guardrail AGENTS.md §3 chiede migrazioni per stato evolvibile).
4. Cambio policy `test-affected` fuori tema (FACT).
5. Quinta fonte di verità semantica da mantenere sincrona (mitigata da validazione strict) (FACT).

## Requisiti riclassificati per fase (correzione 2026-07-19)

La versione originale di questa analisi elencava la telemetria ambiguity tra i guardrail "prima del merge o immediatamente dopo". Riclassificazione corretta: con gate `enabled:false` e resolver non cablato, il merge non attiva nulla — la telemetria è un prerequisito della **fase shadow**, non del merge.

| Fase | Requisito | Task |
| --- | --- | --- |
| **Pre-merge (davvero)** | Decidere sul cambio fuori-tema a `test-affected.mjs` (revert nel branch o motivazione esplicita): impatta la CI dal giorno del merge | SKAUD-019 |
| **Pre-merge (davvero)** | Dichiarare nel codice che il policy resolver è scaffolding non cablato (header comment, costo XS): evita fraintendimenti alla review del merge stesso | SKAUD-004 |
| **Post-merge, pre-shadow** | Cablare `routing.ambiguity` (+ sintesi candidateScores) nella telemetria: senza, la shadow mode non produce dati | SKAUD-003 |
| **Post-merge, pre-shadow** | Version + nota di governance per `mcp-availability.json` | SKAUD-020 |
| **Pre-evaluator** (prima di implementare il client) | Minimizzazione/redazione del prompt inviato (oggi solo nel design doc); transport unico OpenAI-compatible con timeout, cache, circuit breaker; test di integrazione su provider failure/timeout/output invalido da processo reale | — (nuovo lavoro, fuori backlog audit) |
| **Pre-boost** (prima di mode=boost-only) | Dati shadow sufficienti a tarare le soglie del gate; eval di stabilità ABL-09/10 verdi; wiring del resolver nel hook con telemetria delle decisioni | SKAUD-014 (parte) |

## Verdetto branch (motivato)

**READY_WITH_GUARDRAILS.** Il branch è a rischio molto basso perché non cambia il comportamento di produzione (equivalenza testata, flag off, zero rete). I guardrail *pre-merge* effettivi sono solo due e a costo minimo: decisione su `test-affected.mjs` (SKAUD-019) e dichiarazione dello scaffolding (SKAUD-004). Tutto il resto è sequenziato per fase come sopra. Nessuna area è BLOCKING.
