# Log troppo lunghi in Claude Code? I limiti di Context Diet nel conservare gli errori

> Canonical: https://vxi.me/it/posts/claude-code-context-diet-log-compression/
> Locale: it-it
> Published: 2026-10-11
> Updated: 2026-10-11
> Markdown: https://vxi.me/it/posts/claude-code-context-diet-log-compression.md

Quando si avvia `npm test` o `pytest` da Claude Code, centinaia di righe `PASS` possono occupare il contesto mentre l'errore importante resta nascosto in mezzo. L'agente ripete il comando oppure torna a leggere tutto il log per trovarlo. Mostrare soltanto le ultime righe non è una garanzia: l'eccezione potrebbe essere molto prima.

Ho trovato [Context Diet su Hacker News](https://news.ycombinator.com/item?id=50024608). È un Mod per Claude Code che trasforma le lunghe risposte degli strumenti in riepiloghi più brevi, lasciando un percorso per recuperare l'output integrale. L'idea mi piaceva: dare all'agente una specie di indice invece di costringerlo a rileggere continuamente rumore. Nel README, però, compare la promessa «every failure». Come si fa a conservare qualunque numero di errori distinti in uno spazio limitato?

Ho quindi fissato la ricerca alla [versione 0.3.0, commit `689284a`](https://github.com/drkokorev/context-diet/tree/689284a893f1aa509b9c6c1c06b91ef1ca52279c), esaminato Hook, compressore e test e adattato manualmente a JavaScript il percorso generico di riduzione dei log, per provarlo con dati artificiali.

Una distinzione fondamentale: **non ho installato né eseguito Context Diet come Mod dentro Claude Code, e non ho effettuato A/B test sui modelli Claude.** Le misure di seguito descrivono soltanto un algoritmo derivato dal sorgente applicato a log sintetici, non una percentuale reale di errori persi dal plugin né un risparmio di token.

## Quali output vengono abbreviati, e dove finisce l'originale

L'[Hook `session.append`](https://github.com/drkokorev/context-diet/blob/689284a893f1aa509b9c6c1c06b91ef1ca52279c/plugins/context-diet/hooks/register.tsx) del commit fissato gestisce risposte sufficientemente lunghe di Bash, Grep, Glob e contenuti MCP simili a JSON. Read, Edit, Write, WebFetch e testo MCP non JSON non passano da questa specifica compressione. La soglia predefinita è di **8.000 caratteri**, non di 8.000 token.

Non è un semplice `tail` applicato ovunque. I [parser presenti nel sorgente](https://github.com/drkokorev/context-diet/blob/689284a893f1aa509b9c6c1c06b91ef1ca52279c/plugins/context-diet/hooks/tools.ts) riconoscono Jest, Vitest, pytest, Go test, Cargo, i controlli TypeScript e altri formati. Danno precedenza a fallimenti e posizioni diagnostiche, riducendo le righe dei test superati. JSON conserva una struttura essenziale; diff, grep e percorsi di file seguono altre regole. Si tratta di **comportamenti rilevati dalla lettura del codice**, non di compatibilità provata personalmente eseguendo tutti i framework dentro Claude Code.

La scelta più utile, secondo me, è conservare la via di ritorno. Prima di sostituire il risultato con il riepilogo, il Mod tenta di salvarne il testo completo in `.context-diet/out-NNN.txt`, poi ne indica il percorso. I nomi ruotano su **300 slot**. Se il salvataggio fallisce, il codice lascia passare l'output originale anziché presentare un collegamento a un file inesistente.

Ma «salvato sul disco» non significa «archiviato per sempre». Gli slot vengono riutilizzati e nei log potrebbero restare credenziali, variabili d'ambiente o percorsi privati. `.gitignore` può evitare un commit accidentale; non sostituisce cifratura e backup. Per un incidente in produzione è meglio conservare il log originale altrove.

## Dare priorità agli errori non significa mostrarli tutti

Sono andato a leggere [`dietLog` in `diet.ts`](https://github.com/drkokorev/context-diet/blob/689284a893f1aa509b9c6c1c06b91ef1ca52279c/plugins/context-diet/hooks/diet.ts). La funzione raggruppa righe ripetitive, riserva spazio all'inizio e alla fine, poi cerca blocchi di errori partendo da quelli più recenti e prova a includere il contesto vicino.

Alla fine incontra questo controllo molto concreto:

```ts
const cost = window.reduce((n, i) => n + (folded[i]?.text.length ?? 0) + 1, 0)
if (used + cost > target) continue
```

Se un blocco di errore non entra nel budget residuo, viene saltato. Con un solo errore questa priorità può funzionare bene; con decine di errori differenti e stack trace lunghi, un riepilogo di lunghezza finita non li può contenere tutti.

I risultati riconosciuti di Jest o pytest vengono trattati da parser dedicati, con criteri differenti ma comunque limitati. **L'esperimento che segue riguarda soltanto il percorso generico di fallback dei log; non misura quanti errori conservano i parser specifici di Jest o pytest.** Leggere il risultato come «il plugin perde il 95% dei fallimenti Jest» sarebbe scorretto.

«Every failure» può essere inteso, alla luce del codice, come «mostrare prima i fallimenti e mantenere accessibile il risultato completo», non come garanzia che ogni riga d'errore sia sempre visibile in un riepilogo breve.

## Un errore centrale, poi 145 errori diversi

Non volevo mettere in piedi un'intera installazione di Claude Code per osservare un vincolo sulla lunghezza. Ho adattato manualmente `dietLog` e gli helper di testo in JavaScript senza dipendenze e li ho eseguiti in ambiente isolato con Node.js v22.16.0. La suite sintetica più completa ha concluso **4 test su 4, exit code 0**. Non è stata invocata l'API dei Mod.

| Input artificiale | Lunghezza originale | Lunghezza riepilogo | Osservazione |
| --- | ---: | ---: | --- |
| Log lungo con un errore in posizione centrale | 32.087 caratteri | 1.175 caratteri | conservato 1/1 marcatore |
| 145 errori differenti, obiettivo di 1.450 caratteri | 23.146 caratteri | 1.419 caratteri | visibili 7/145 righe originali d'errore |
| Output corto | 45 caratteri | 45 caratteri | invariato |
| Controllo `PASS errorHandler.test.ts` | 52 caratteri | 52 caratteri | PASS non classificato come errore |

Nel secondo caso, **138 righe d'errore originali non comparivano nel riepilogo visibile**. Ecco una parte dell'output *sintetico* effettivamente salvato: non è un traceback proveniente da un progetto reale.

```text
… [225 lines cut] …
ERROR CASEei unhandled category_ts in /app/modules/amy/workflow.ts:40 expected stable result bci
ERROR CASEej unhandled category_tt in /app/modules/amz/workflow.ts:40 expected stable result bcj
ERROR CASEek unhandled category_tu in /app/modules/ana/workflow.ts:40 expected stable result bck
…
… [79 lines cut] …
```

C'è un dettaglio che merita attenzione. Il compressore scriveva «5 of 145 error lines with context», ma contando tutte le righe originali d'errore presenti nel testo finale se ne trovavano **sette**. Anche le parti iniziale e finale preservate separatamente possono contenere un errore. «Blocchi di errore selezionati» e «righe d'errore visibili» non sono lo stesso conteggio.

**Il budget di 1.450 caratteri è stato volutamente ridotto per questo esperimento sintetico: non è quello predefinito del plugin.** Nel codice v0.3.0 la soglia che attiva la riduzione è inizialmente di **8.000 caratteri**. Per un output lungo normale, il target del riepilogo è `Math.max(2_000, Math.round(threshold / 2))`, quindi **4.000 caratteri**. Se Claude Code aveva già salvato l'output in un file, il target diventa **3.000 caratteri**. Più spazio può contenere più informazioni, ma il mio esperimento non permette di stimare percentuali reali di errori omessi.

Ho eseguito anche una riproduzione più piccola, composta da **due test con la sola libreria standard di Node.js**, che isola la selezione per budget. Le righe d'errore artificiali sono leggermente più lunghe: con 1.450 caratteri rimangono **8/145**, anziché sette. I **2/2 test passano**. Sono input e implementazioni diverse, non due condizioni di un benchmark da confrontare.

Qui sotto c'è il codice completo della riproduzione minima già verificata. Salvalo come `budget-replay.test.mjs` ed esegui `node --test budget-replay.test.mjs`:

```js
/**
 * First-party negative control for Context Diet v0.3.0's generic dietLog budget path.
 * Pin: drkokorev/context-diet @ 689284a893f1aa509b9c6c1c06b91ef1ca52279c.
 * Only the error-window budget logic is mirrored, for controlled ASCII log lines.
 * This is NOT an upstream Mod invocation, a benchmark, or a production loss rate.
 * The full adapted dietLog fixture and observed digests are in the separate evidence bundle.
 */
import assert from 'node:assert/strict';
import test from 'node:test';

const id = i => { let s='',n=i+1; while(n){ n--;s=String.fromCharCode(97+n%26)+s;n=Math.floor(n/26); } return s; };
const noise = n => Array.from({length:n},(_,i)=>'step '+id(i)+' completed '+id(i+1200)+' '+i);
const critical = line => /^ERROR\b|^\s+(Expected|Received):/.test(line);
function select(raw,target){
  const folded=raw.split('\n').map(text=>({text,level:critical(text)?2:0}));
  const errorHits=folded.map((l,i)=>l.level===2?i:-1).filter(i=>i>=0);
  const keep=new Set();let used=0;
  const headBudget=target*(errorHits.length?0.1:0.2);
  const tailBudget=target*(errorHits.length?0.25:0.35);
  for(let i=0;i<folded.length&&used<headBudget;i++){keep.add(i);used+=folded[i].text.length+1;}
  let tailUsed=0;
  for(let i=folded.length-1;i>=0&&tailUsed<tailBudget;i--){
    if(!keep.has(i)){keep.add(i);tailUsed+=folded[i].text.length+1;}
  }
  used+=tailUsed;
  let windowsShown=0;
  for(let h=errorHits.length-1;h>=0;h--){
    const at=errorHits[h];const window=[];
    for(let i=at-2;i<=at+3;i++)if(i>=0&&i<folded.length&&!keep.has(i))window.push(i);
    const cost=window.reduce((n,i)=>n+folded[i].text.length+1,0);
    if(used+cost>target)continue; // EXACT budget guard from upstream dietLog.take()
    window.forEach(i=>keep.add(i));used+=cost;windowsShown++;
  }
  return {visible:folded.filter((_,i)=>keep.has(i)).map(l=>l.text).join('\n'),totalCritical:errorHits.length,windowsShown};
}
test('single late/central failure remains visible',()=>{
  const text=[...noise(310),'ERROR ECASEONE impossible transaction transition','  Expected: completed','  Received: pending',...noise(310)].join('\n');
  const out=select(text,2000);
  assert.ok(out.visible.includes('ECASEONE'));
});
test('many distinct failures cannot all fit finite digest budget',()=>{
  const failures=Array.from({length:145},(_,i)=>'ERROR CASE'+id(i)+' unhandled category_'+id(i+400)+' in /app/modules/'+id(i+900)+'/workflow.ts:40 expected stable result '+id(i+1300));
  const raw=[...noise(90),...failures,...noise(90)].join('\n');
  const out=select(raw,1450);
  const retained=failures.filter(line=>out.visible.includes(line)).length;
  assert.ok(retained>0&&retained<failures.length);
  process.stdout.write(JSON.stringify({failure_lines:failures.length,retained,missing:failures.length-retained,digest_budget_chars:1450,mode:'source-budget-window-control-not-plugin-runtime'})+'\n');
});
```

Questo script riproduce solo il vincolo di selezione del log generico, senza usare l'API dei Mod di Claude Code. Test verdi non dimostrano che il plugin installato abbia funzionato né che un agente abbia diagnosticato bene il problema.

La conclusione non è che comprimere sia sbagliato. Un riepilogo deve restare **un indice per ritrovare le prove originali, non una prova completa di per sé**.

## Prima dry, poi valuta capture separatamente

Se hai una versione di Claude Code compatibile con Mods, confronta il [README del commit fissato](https://github.com/drkokorev/context-diet/blob/689284a893f1aa509b9c6c1c06b91ef1ca52279c/README.md) con la documentazione attuale del client. L'autore indicava **Claude Code 2.1.287+** per quella versione. Non ho verificato personalmente l'installazione su un client reale.

```text
/plugin marketplace add drkokorev/context-diet
/plugin install context-diet@context-diet
```

Partirei da `/diet dry` e `/diet report`, così si osserva che cosa verrebbe ridotto senza sostituire il testo originale. Per un comando che richiede un log completo si può usare `/diet keep <frammento comando>` oppure spegnere la funzione con `/diet off`.

`/diet capture on` affronta un problema diverso: Claude Code può aver troncato già da sé un risultato molto lungo. Capture è **disabilitato in partenza**. Per alcuni singoli comandi Bash di test o build, riscrive il comando per raccogliere stdout e stderr insieme in `.context-diet/run-NN.log`, rileggerli e cercare di mantenere il codice di uscita.

Questo involucro può smettere di corrispondere a regole di permesso già approvate, come `Bash(pytest:*)`, e provocare una nuova richiesta di autorizzazione. La fusione dei flussi può modificarne l'ordine osservabile. Pipeline, comandi concatenati, redirezioni preesistenti, modalità watch e processi in background non sono gestiti dalla cattura automatica. In un sistema attento ai permessi o dipendente dall'output in tempo reale, non la attiverei senza prova. Si torna indietro con `/diet capture off`.

Nel [README dell'autore](https://github.com/drkokorev/context-diet/blob/689284a893f1aa509b9c6c1c06b91ef1ca52279c/README.md) sette compiti di domanda-risposta risultano corretti in entrambi i gruppi, con chiamate agli strumenti scese **da 13 a 8**. In altri **18 tentativi di correzione da parte di agenti**, vengono riportate medie di **8,0 senza**, **6,0 con** e **5,8 con capture**. Sono risultati aggregati e auto-riportati su un campione limitato, non una mia replica dei run, dei costi o dei successi.

Per sessioni sommerse da test superati, Context Diet è uno strumento da valutare. Ma durante indagini su incidenti, errori concorrenti o decisioni di merge automatico, non bisogna dedurre che l'elenco dei problemi sia completo soltanto dal riepilogo. L'agente tornerà a leggere il file originale oppure correggerà solo i pochi errori visibili per poi aprire una PR? È una domanda sul suo comportamento che qui non ho misurato.

In passato ho esaminato [come dividere le istruzioni tra CLAUDE.md, AGENTS.md e Skills](https://aitokenhacks.com/posts/claude-md-agents-md-skills-ci/) e [i passaggi di stato tra agenti](https://aitokenhacks.com/posts/ai-agent-orchestration-after-assistants/). Là il punto era che cosa caricare e chi possiede il compito. Qui è qualcosa di più immediato: il risultato dello strumento che il modello vede è una prova completa o soltanto un promemoria con il percorso per tornare all'originale?
