Naar inhoud

Cassandra

Onthoudt wat al mislukte, en zegt het voordat je agent het nog eens probeert.

Binnen één context ziet een agent zijn eigen mislukking nog staan. Na een compactie, in een nieuwe sessie of in een verse subagent is die weg, en begint het opnieuw.

Wat het doet

Cassandra is een Claude Code-plugin die meekijkt op de levensloop van een tool-aanroep. Mislukt een Bash- of mcp__*-aanroep, dan legt hij die vast. Draait je agent later dezelfde aanroep opnieuw terwijl er in het project niets veranderd is, dan krijgt het model één regel te lezen: dit is eerder geprobeerd, dit kwam eruit.

Herkennen gaat op een vingerafdruk, niet op tekst. De hash gaat over tool_name en tool_input, allebei gestructureerde JSON. Een Bash-commando wordt getrimd en opeenvolgende spaties worden samengevouwen, verder niets: geen paden normaliseren, geen vlaggen herschikken, geen afsluitende pipes weghalen. Elk van die stappen kan twee verschillende commando's stilletjes tot één maken.

Edit en Write doet hij niet. Die payloads herhalen zich nooit byte voor byte, ook niet als het functioneel dezelfde correctie is, dus indexeren zou een index opleveren die nooit raak schiet.

Waar het zijn plek verdient

Binnen één intacte context is dit bijna overbodig, en dat zegt de plugin zelf ook. Het model ziet zijn eigen mislukking een paar duizend tokens terug in het transcript staan en corrigeert zonder hulp. Cassandra is gebouwd voor de drie grenzen waar dat transcript verdwenen is:

  • Na een compactie. De mislukte aanroep valt uit de context, het record niet.
  • In een nieuwe sessie. De doodlopende weg van gisteren is vandaag onzichtbaar.
  • In een subagent. Die weet niet dat de hoofdagent er al vier aanroepen aan verspild heeft.

Buiten die grenzen houdt hij bewust zijn mond.

Snel starten

bash
claude plugin marketplace add https://aranea-development.nl/plugins/marketplace.json
claude plugin install cassandra@aranea

Hooks binden bij het starten van een sessie, en de hook-binary staat niet in de repository. De eerste sessie na installatie bouwt hem op de achtergrond en zegt dat; vanaf de tweede sessie is de plugin actief. Die binary is ongeveer 79 MB, omdat bun build --compile de Bun-runtime meebakt, en dat is precies waarom hij gebouwd wordt in plaats van meegeleverd. Zonder Bun op je PATH blijft de plugin inert en meldt dat één keer, bij de start van de sessie.

Hoe het beslist of het waarschuwt

Bij PreToolUse gebeuren er drie dingen. De aanroep krijgt een vingerafdruk en die wordt opgezocht; een misser, wat de overgrote meerderheid is, eindigt zwijgend. Bij een treffer draait de versheidstoets. Is er sinds de mislukking iets veranderd, dan is de herhaling terecht en blijft Cassandra stil. Pas als er niets veranderd is, komt die ene regel.

Die versheidstoets is in een git-repository een hash van HEAD plus git status --porcelain. Daarbuiten valt hij terug op een begrensde wandeling over de wijzigingstijden van de werkmap. Kan hij het niet uitmaken, dan zwijgt hij in plaats van te gokken, want een onterechte waarschuwing is erger dan een gemiste.

Er zijn twee gaten en de plugin verzwijgt ze niet. Een bestand dat herschreven wordt naar andere inhoud van dezelfde lengte, met de wijzigingstijd achteraf teruggezet, ontsnapt aan de mtime-variant. En de toets kijkt alleen naar het project: een oplossing die ergens anders landt, een pakket dat globaal geïnstalleerd wordt, een omgevingsvariabele, laat de stempel identiek. Daarom noemt de waarschuwing wat er werkelijk gecontroleerd is, "er is niets veranderd in deze repository", zodat de bewering waar blijft ook waar de toets blind is.

Wat het kost, en wat je ervoor terugkrijgt

Ongeveer 12 ms per hook-aanroep op een rustige machine, 17 ms onder belasting, tegen een ontworpen budget van 20 ms. Maar een tool-aanroep is geen enkele hook-aanroep: PreToolUse en PostToolUse hangen aan dezelfde matcher, dus een geslaagde aanroep start de binary twee keer, samen ongeveer 25 ms. Per aanroep gemeten past dat binnen het budget, per geslaagde tool-aanroep niet.

Die tweede aanroep is een keuze. PostToolUse lost de openstaande markering op, en dat is de enige manier waarop Cassandra kan zien dat een aanroep waarvoor hij waarschuwde daarna gewoon slaagde. Haal die hook weg en het percentage onterechte waarschuwingen in cassandra stats bestaat niet meer, en dat is nu juist het getal waaraan je afleest of de versheidstoets deugt.

cassandra stats toont twee getallen. Het aandeel onterechte waarschuwingen zegt of de toets verbetering nodig heeft. Het aandeel waarschuwingen waarbij er helemaal geen grens gepasseerd werd, zegt iets anders: dan vertelt Cassandra het model dingen die het zelf al kon zien, en het signaal is om hem te verwijderen, niet om hem af te stellen. Een tool die zegt wanneer je hem niet nodig hebt is zeldzaam genoeg om te noemen.

Wat het niet doet

Hij blokkeert niets. Hij weigert niets, herschrijft niets, en het enige wat hij kan toevoegen is één regel additionalContext. Hij leest je prompts niet en het proza van het model evenmin.

Eén stuk vrije tekst bewaart hij wel, en dat is het waard om te weten: een fragment van 240 tekens uit de foutmelding of weigeringsreden van de tool zelf, zodat je in de waarschuwing terugziet waarom de aanroep de vorige keer stierf. Dat fragment komt uit een commando, niet uit jou en niet uit het model, en wordt als onvertrouwd behandeld: stuurtekens gaan eruit voordat het wordt opgeslagen, en in de waarschuwing staat het tussen hekjes met het label tool-uitvoer, niet als instructie.

Verder verlaat er niets je machine. Geen netwerkverkeer, geen telemetrie, geen API-sleutel, en niets buiten zijn eigen datamap.