Wer in Claude Code eine größere Testsuite startet, bekommt schnell Hunderte Zeilen mit PASS, Build-Meldungen und Warnungen. Der eigentliche Fehler steht womöglich irgendwo dazwischen. Der Agent startet den Test erneut oder liest das ganze Log, nur um diese Stelle zu finden. Die letzten zwanzig Zeilen zu zeigen ist keine zuverlässige Lösung: Ein wichtiger Stacktrace kann viel früher stehen.

Über einen Show-HN-Beitrag bin ich auf Context Diet gestoßen. Das Projekt nutzt einen Claude-Code-Mod-Hook, um lange Tool-Antworten vor dem Gesprächskontext zusammenzufassen und gleichzeitig einen Pfad zum vollständigen Original zu hinterlassen. Die Idee gefiel mir zunächst: eine kurze Orientierung mit Rückweg zur Quelle statt immer wieder denselben Ausgabemüll zu lesen.

Im README steht allerdings „every failure“. Das hat mich stutzig gemacht. Wie soll eine Zusammenfassung mit begrenztem Zeichenbudget beliebig viele unterschiedliche Fehler vollständig zeigen? Ich habe deshalb v0.3.0, Commit 689284a festgehalten, die Hooks, Parser und Tests gelesen und den allgemeinen Log-Reduzierer für einen eigenständigen JavaScript-Test mit künstlich erzeugten Logs nachgebildet.

Wichtige Einschränkung: Ich habe Context Diet nicht als Mod in einer echten Claude-Code-Sitzung installiert oder ausgeführt und keinen Modell-A/B-Test durchgeführt. Die folgenden Zahlen stammen aus synthetischen, quellcodebasierten Algorithmus-Tests. Sie belegen weder eine reale Fehlerrate des Plugins noch eingesparte Token oder bessere Agent-Ergebnisse.

Was wird gekürzt, und wie kommt man an das Original?

Der session.append-Hook bearbeitet geeignete lange Tool-Resultate, insbesondere Bash, Grep, Glob und JSON-artige MCP-Antworten. Read, Edit, Write, WebFetch und normaler MCP-Fließtext fallen nicht unter diesen Kürzungspfad. Die Standardschwelle liegt bei 8.000 Zeichen, nicht 8.000 Token.

Die Kürzung ist nicht einfach ein pauschales tail. Die Parser im fixierten Quellstand erkennen unter anderem Jest, Vitest, pytest, Go test, Cargo und TypeScript-Checker und priorisieren Fehlerstellen und Diagnosekontext gegenüber Erfolgsmeldungen. JSON wird auf ein strukturelles Gerüst reduziert; für Diffs, Grep und Dateipfade gibt es weitere Regeln. Das sind im Code nachvollzogene Verfahren, keine von mir ausgeführten Laufzeittests sämtlicher Testframeworks innerhalb von Claude Code.

Entscheidend finde ich den Rückweg. Vor dem Austausch gegen ein Digest versucht der Hook, den gesamten Inhalt unter .context-diet/out-NNN.txt zu speichern, und nennt anschließend diesen Pfad. Die Namen laufen über 300 rotierende Slots. Scheitert das Schreiben, wird die originale Tool-Antwort unverändert durchgereicht – statt auf eine nicht vorhandene Datei zu verweisen.

Die Dateien sind aber kein dauerhaftes Archiv. Slots werden überschrieben; API-Schlüssel, Umgebungsvariablen oder Projektpfade können auf der Festplatte landen. Eine .gitignore verhindert weder unbefugten Zugriff noch ersetzt sie Verschlüsselung und Backups. Für Vorfälle in Produktion muss das Originallog gesondert gesichert sein.

Fehler priorisieren heißt nicht, jeden Fehler zu zeigen

Im allgemeinen Log-Reduzierer dietLog werden ähnliche Zeilen zusammengefasst, Zeichen für Kopf und Ende reserviert und von hinten nach Fehlerfenstern gesucht. Nach Möglichkeit bleiben einige Zeilen Kontext erhalten.

Dann folgt eine sehr nüchterne Budgetregel:

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

Passt ein Fehlerfenster samt Kontext nicht in das Restbudget, wird es übersprungen. Bei nur einem Fehler kann die Priorisierung sehr hilfreich sein. Bei Dutzenden unterschiedlichen Fehlern und langen Stacktraces reicht ein endliches Digest nicht für alles.

Für erkannte Jest- und pytest-Ausgaben existieren eigene Parser mit anderen, ebenfalls begrenzten Auswahlregeln. Mein Experiment betrifft nur den allgemeinen Fallback für Logs und misst nicht die Fehlererhaltung dieser spezialisierten Parser. Aus den Zahlen abzuleiten, das Plugin verliere etwa 95 Prozent realer Jest-Fehler, wäre unzulässig.

„Every failure“ lässt sich anhand der Implementierung höchstens sinnvoll als „Fehler bevorzugt sichtbar machen und das vollständige Ergebnis erreichbar halten“ verstehen. Es ist keine Garantie, dass jede einzelne Fehlerzeile bei jeder möglichen Eingabe in einem kurzen Digest landet.

Ein Fehler in der Mitte – und 145 verschiedene Fehler

Ich habe den allgemeinen dietLog-Pfad und benötigte Text-Helfer aus dem fixierten TypeScript-Quellstand manuell nach JavaScript übertragen und mit künstlichen Strings unter Node.js v22.16.0 isoliert ausgeführt. Diese größere synthetische Suite endete mit 4/4 bestandenen Tests, Exitcode 0. Die Claude-Code-Mod-Integration wurde dabei nicht aufgerufen.

Künstlicher TestfallUrsprüngliche LängeDigest-LängeBeobachtung
Langes Log mit einem Fehler in der Mitte32.087 Zeichen1.175 Zeichen1/1 Fehlermarker sichtbar
145 unterschiedliche Fehler, Zielbudget 1.450 Zeichen23.146 Zeichen1.419 Zeichen7/145 Original-Fehlerzeilen sichtbar
Kurze Ausgabe45 Zeichen45 Zeichenunverändert
Kontrollzeile PASS errorHandler.test.ts52 Zeichen52 ZeichenPASS nicht als Fehler eingestuft

Im zweiten Fall tauchten 138 ursprüngliche Fehlerzeilen nicht im sichtbaren Digest auf. Hier ein tatsächlicher Auszug der gespeicherten synthetischen Ausgabe – kein Stacktrace aus einem Kundenprojekt:

… [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] …

Eine kleine Zählfalle: Der Reduzierer meldete „5 of 145 error lines with context“. Zählt man aber alle originalen Fehlerzeilen, die im fertigen Digest zu sehen sind, erhält man sieben. In dem ohnehin erhaltenen Kopf- oder Schlussabschnitt können ebenfalls Fehler stehen. Die Zahl ausdrücklich ausgewählter Fehlerfenster ist nicht gleich der Zahl sichtbarer Fehlerzeilen.

Das Zielbudget von 1.450 Zeichen wurde für diesen synthetischen Stresstest bewusst knapp gewählt. Es ist nicht der Plugin-Standard. Bei der fixierten Version beträgt die Auslöseschwelle standardmäßig 8.000 Zeichen. Ein normales langes Ergebnis bekommt über Math.max(2_000, Math.round(threshold / 2)) ein Digest-Ziel von 4.000 Zeichen; hatte Claude Code die Ausgabe bereits als Datei gespeichert, sind es 3.000 Zeichen. Ein größeres Budget kann mehr Kontext aufnehmen. Daraus ergibt sich trotzdem keine belastbare Fehlerrate in echten Projekten.

Zusätzlich habe ich nur die Budget-Auswahl in einem kleinen, allein mit der Node-Standardbibliothek lauffähigen Zwei-Test-Skript nachgestellt. Dessen künstliche Fehlerzeilen sind etwas länger; bei 1.450 Zeichen blieben 8/145 sichtbar. Beide Tests bestanden (2/2). Die sieben aus der umfangreicheren Anpassung und die acht aus diesem kleineren Beispiel sind Ergebnisse unterschiedlicher Eingaben und dürfen nicht wie zwei Performance-Konfigurationen verglichen werden.

Hier steht die komplette geprüfte kleine Reproduktion. Als budget-replay.test.mjs speichern und mit node --test budget-replay.test.mjs ausführen:

/**
 * 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');
});

Das Skript bildet die Budgetregel für den allgemeinen Logpfad ab. Es verwendet weder die Mod-API noch einen Claude-Agenten. Ein grüner Test beweist nicht, dass das eigentliche Plugin erfolgreich integriert wurde oder ein Agent die Fehler richtig diagnostiziert hätte.

Für mich ist das keine Widerlegung von Log-Kompression. Aber ein Digest muss ein Index zurück zum Beleg bleiben und darf nicht als vollständiger Beleg missverstanden werden.

Erst dry, capture nur nach eigener Prüfung

Wer Claude Code mit Mod-Unterstützung verwendet, sollte das README der fixierten Version mit der aktuellen Client-Dokumentation vergleichen. Der Autor nannte damals Claude Code 2.1.287+. Die Installation habe ich nicht in einer echten Sitzung überprüft.

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

Ich würde mit /diet dry anfangen, typische Tests laufen lassen und über /diet report nachsehen, was gekürzt worden wäre. Dry soll die Originalausgabe nicht ersetzen. Wo ein vollständiges Log Pflicht ist, kann /diet keep <Befehlsfragment> die Kürzung für passende Kommandos aussetzen oder /diet off die Funktion deaktivieren.

/diet capture on löst ein anderes Problem: Claude Code kann die Ausgabe bereits selbst gekürzt haben, bevor der Hook sie verarbeitet. Capture ist standardmäßig aus. Für bestimmte einfache Test- oder Build-Befehle wird der Bash-Aufruf umgeschrieben, stdout/stderr gemeinsam nach .context-diet/run-NN.log geleitet, danach zurückgelesen und der Exitcode nach Möglichkeit bewahrt.

Das kann die bisherige Berechtigungsregel, etwa Bash(pytest:*), nicht mehr passend erscheinen lassen und eine neue Nachfrage auslösen. Das Zusammenlegen der Ausgabeströme kann die sichtbare Reihenfolge beeinflussen. Pipes, Befehlsverkettung, bereits vorhandene Umleitungen und Watch-/Hintergrundmodi fallen aus dem automatischen Capture heraus. Bei engen Berechtigungsregeln oder auf Live-Ausgabe angewiesenen Workflows würde ich die Funktion nicht ungeprüft einschalten. /diet capture off schaltet sie wieder aus.

Das README des Autors berichtet bei sieben Frage-Antwort-Aufgaben volle Korrektheit in beiden Gruppen, aber 13 statt 8 gesamte Tool-Aufrufe. Für 18 Agent-Bugfix-Läufe lauten die gemeldeten Mittelwerte 8,0 ohne, 6,0 mit und 5,8 mit Capture. Das sind kleine, vom Autor berichtete Aggregate. Ich habe weder die Modellläufe noch Token-Kosten oder Erfolgsquoten unabhängig reproduziert.

Wenn erfolgreiche Tests ständig den Kontext füllen, könnte Context Diet einen Versuch wert sein. Bei Vorfallanalysen, konkurrierenden Fehlern oder automatischen PR-Merges muss der Agent vor endgültigen Entscheidungen aber das vollständige Log nachlesen. Ob er das tatsächlich tut oder nur die wenigen sichtbaren Fehler behebt und zu früh einen PR erstellt, bleibt eine offene Frage über sein Verhalten – kein Ergebnis meines Tests.

Meine früheren Texte über die Aufteilung von CLAUDE.md / AGENTS.md / Skills und Zustandsübergaben zwischen Agenten behandelten, was geladen wird und wem eine Aufgabe gehört. Hier geht es um etwas Kleineres: Ist die Tool-Ausgabe vor dem Modell die vollständige Evidenz oder nur ein Zettel mit dem Weg zurück?