Vox' AGENTS.md-Zeilen in 90 Claude-Code-Läufen getestet: Die Zeile, die am besten wirkte, stammte nicht von ihm
Zeile eins brachte den Agenten dazu, die Eingabeprüfung wegzulassen. Was Zeile zwei zusätzlich einbaute, verschwand, sobald Zeile eins mit in der Datei stand. „Without breaking anything“ zeigte keinen klaren Effekt über das hinaus, was ein genannter Testbefehl schon leistete. Was 90 Läufe aussagen können und was nicht, und wie du eine Zeile testest, bevor du sie behältst.
Inhalt
Am 22. Oktober postete Vox zwei Zeilen, die Vibecoder in ihre AGENTS.md kopieren sollten. Als ich den Post zwei Tage später festhielt, stand er bei 532.100 Aufrufen und rund 11.000 Lesezeichen. Einen Tag nach dem ersten Post folgte eine dritte Zeile. Die Antworten teilten sich wie immer: Die einen bedankten sich für den Tipp, die anderen meinten, so eine Zeile bewirke gar nichts.
Einen Diff zeigte keine der beiden Seiten. Also habe ich die Zeilen laufen lassen: zwei kleine Aufgaben, zwei Claude-Modelle, ein Projekt ohne AGENTS.md, eines mit einer schlichten Basis-AGENTS.md und eines mit der Basis plus Vox’ Zeilen, zusammen und einzeln. Insgesamt neunzig Läufe. Die erste Zeile brachte den Agenten dazu, die Eingabeprüfung wegzulassen. Die zweite fügte allein ein zusätzliches Screenreader-Attribut hinzu, und das verschwand, sobald die erste Zeile mit in der Datei stand. Und die Zeile, die die Gewohnheiten des Agenten am zuverlässigsten änderte, stammte gar nicht von Vox. Es war die langweilige, die den Testbefehl nennt.
Was sind Vox’ AGENTS.md-Zeilen?
Drei Sätze, gedacht für die AGENTS.md jedes beliebigen Projekts. Die ersten beiden kamen zusammen am 22. Oktober:
Cut taglines, helper text and secondary labels. If a screen needs them to make
sense, fix the layout.
Make the best decision that will elevate all three: the user experience (UX),
developer experience (DX), and agent experience (AX) always, without breaking
anything.Die dritte kam am 23. Oktober, angepriesen als Mittel gegen einen Agenten, der „reflexartig Code für Race Conditions und Sicherheitsprobleme einbaut, die fast nie auftreten“:
Before you add code for a race condition or security edge case that is unlikely
or purely hypothetical, tell me exactly what would go wrong, how likely that is,
and how much complexity the extra code adds, in words a 10-year-old could
understand. Then wait for my OK. Don't quietly widen the scope to guard against
problems that only exist in theory.AGENTS.md selbst ist eine schlichte Markdown-Datei ohne Pflichtfelder. Die Seite agents.md nennt sie ein README für Agenten und gibt genau eine Vorrangregel an: „Die AGENTS.md, die der bearbeiteten Datei am nächsten liegt, gewinnt; explizite Prompts des Nutzers im Chat haben Vorrang vor allem.“ Claude Code liest sie standardmäßig nur, wenn es im Arbeitsverzeichnis oder darüber keine CLAUDE.md, .claude/CLAUDE.md oder CLAUDE.local.md gibt, ein Fallback, kein Zusammenführen. Anthropic sagt offen, wie beide Dateien behandelt werden: Claude liest sie „als Kontext, nicht als erzwungene Konfiguration“.
Wie habe ich die AGENTS.md-Zeilen in Claude Code getestet?
Mit einem kleinen Wegwerfprojekt, zwei Aufgaben und der Anweisungsdatei als einzigem Unterschied zwischen den Läufen. Das Projekt ist eine statische Seite mit einem vorhandenen Rechner (Rohrgewicht: zwei beschriftete Eingabefelder, ein Button, eine Ausgabe, kein Hilfetext) und ein winziger Python-Server, dessen get_user aus einer Fake-Datenbank liest, die pro Abfrage 200 ms braucht. Zwei Tests laufen grün. Der Docstring des Servers sagt, dass er absichtlich single-threaded ist.
Es gab fünf Bedingungen. Gar keine AGENTS.md. Eine Basis-AGENTS.md aus drei kurzen Zeilen: was das Projekt ist, „Python 3, standard library only“ und „Run the tests with python3 -m pytest -q.“ Die Basis plus alle drei Zeilen von Vox im Wortlaut. Und die Basis plus nur Zeile 2, bei beiden Aufgaben, oder nur Zeile 1, nur bei der UI-Aufgabe. Die beiden Prompts:
ui: Add a cylinder tank volume calculator to static/index.html: the user
enters the tank's diameter and height in centimetres and sees the
volume in litres.
cache: get_user in users.py is slow because every call hits the database.
Add a cache so that looking up the same user again within 60 seconds
doesn't hit the database.Jeder Lauf war eine frische Kopie des Projekts und eine frische Claude-Code-2.1.310-Session im Print-Modus (claude -p), fünf Läufe pro Zelle, mit Opus 5.5 und Sonnet 5.5; Dateiänderungen wurden automatisch akzeptiert. Read, Edit, Write, Glob, Grep und eine kurze Bash-Allowlist mit python3 waren vorab freigegeben, und andere Tools hat kein Lauf benutzt.
Auf meinem Rechner liegt eine CLAUDE.md auf Nutzerebene voller Regeln zu Kürze und Umfang, und Claude Code lädt diese Datei neben jeder AGENTS.md. Wäre sie drin geblieben, hätte sie jede Bedingung verwischt. Deshalb zeigte jeder Lauf mit CLAUDE_CONFIG_DIR auf ein separates Konfigurationsverzeichnis, in dem anfangs nur der Login lag. Eine Canary-Regel in einer Test-AGENTS.md zeigte, was das entfernt und was nicht: Diese Session sah die AGENTS.md des Projekts und keine Nutzer-CLAUDE.md und kein Memory, während Plugins und Connectors, die am Account hängen, weiterhin in ihrer Tool-Liste auftauchten.
Drei Einschränkungen zur Methode. Was gezählt wird, habe ich vor dem ersten Lauf notiert, aber die Zahlen zu Fehlermeldungen und Screenreader-Attributen unten stammen aus dem Lesen der Diffs, und die Bedingungen mit einzelnen Zeilen kamen erst hinzu, nachdem ich die ersten vierzig gelesen hatte. Ein Bug in meinem Run-Skript schickte die erste Basis ohne ihre AGENTS.md los; ein Reviewer bemerkte das, diese zwanzig Läufe wurden zur Bedingung ohne Datei, und die Basis lief noch einmal. Und im Print-Modus ist niemand da, der eine Frage beantwortet, was für Zeile 3 eine Rolle spielt. Neunzig Läufe kosteten zum Listenpreis 16,82 $, im Median 23 Sekunden pro Lauf.
Entfernt „cut helper text“ in der AGENTS.md Beschriftungen, die ein Eingabefeld definieren?
Nein. Alle 50 UI-Läufe beschrifteten das Feld mit „Diameter (cm)“, egal, was in der AGENTS.md stand. Mit Zeile 1 fehlten die Meldung bei falscher Eingabe und die Prüfung dahinter.
Der Rechner war meine Antwort auf die schärfste Reaktion unter Vox’ Post, von jemandem, der Rechner entwickelt:
One exception for calculators: labels that define the input aren’t clutter.
Confusing radius with diameter changes the area by 4×.Diese Befürchtung hat sich hier nicht bestätigt. Das Gerümpel allerdings auch nicht: Kein Lauf in irgendeiner Bedingung fügte einen Placeholder, einen Hinweis, einen Untertitel oder einen Tooltip hinzu. Der vorhandene Rohrgewicht-Rechner hatte nichts davon, und jeder Lauf übernahm seine Form. Zeile 1 hatte keinen Hilfetext zu streichen, weil ohnehin kein Lauf welchen hinzufügen wollte.
Geändert hat sich der Fehlerpfad. Mit der Basis-AGENTS.md prüften 9 von 10 Läufen die Eingabe und schrieben für den leeren oder negativen Fall einen Satz wie „Enter a positive diameter and height“. Mit Zeile 1 dazu tat das 0 von 10, und keiner der zehn prüfte die Eingabe überhaupt. Klick auf einem leeren Formular auf Calculate, und jeder von ihnen zeigt NaN L. Mit allen drei Zeilen hatten 0 von 10 Läufen eine Meldung. Vier zeigten NaN L, sechs prüften die Eingabe, ließen die Ausgabe aber leer.
Meine Deutung, die die Transkripte nicht beweisen können: Eine Fehlermeldung sieht einem erklärenden Text ähnlich genug, um unter eine Regel über erklärende Texte zu fallen. So oder so hat eine Zeile, die auf Gerümpel zielte, das Formular genau den einen Satz gekostet, den es braucht.
Ist „without breaking anything“ in der AGENTS.md das neue „make no mistakes“?
Was die Verifikation angeht, größtenteils ja. In diesen Läufen zeigte die Zeile keinen klaren Effekt über das hinaus, was der Testbefehl schon bewirkte. Unter Vox’ Post fragte genau das die meistgelikte Antwort, die ich festgehalten habe (328 Likes), und eine andere meinte, der Agent werde „einfach sagen, er habe alle drei verbessert“.
Von diesen beiden Vorhersagen traf die zweite nicht ein: Kein Lauf behauptete, UX, DX oder AX verbessert zu haben, 0 von 90. Die erste traf weitgehend zu. Bei der Cache-Aufgabe sorgte schon die Basis-AGENTS.md allein in 10 von 10 Läufen dafür, dass die Tests liefen, also hatte Zeile 2 dort keinen Spielraum, etwas hinzuzufügen. Der sichtbare Unterschied lag zwischen keiner Datei und der Basis. Ohne AGENTS.md ließ Sonnet in 5 von 5 Cache-Läufen keine Tests laufen und schrieb auch keine. Opus schrieb Tests, versuchte es aber mit python -m pytest, das es auf diesem Rechner nicht gibt, und hörte ungetestet auf. Sobald die Datei python3 -m pytest -q nannte, ließen beide Modelle den Befehl laufen. Die Basis änderte mehr als diesen einen Satz, das ist also meine Schlussfolgerung, aber es ist der Satz, der den Befehl nennt.
Bei der UI-Aufgabe, bei der die Python-Tests mit der Änderung an HTML und JavaScript nichts zu tun haben, ließ Opus sie trotzdem laufen, in 4 von 5 Basis-Läufen und in 5 von 5 mit Zeile 2. Sonnet tat es in 0 und 1 von 5. Zehn Läufe können das nicht von Rauschen unterscheiden. Die meisten dieser Läufe hatten außerdem einen Node-Check der Formel versucht, den meine Allowlist ablehnte, also lief der relevante Check nie und der irrelevante schon. Ein Lauf mit Zeile 2 sagte das ganz offen:
The existing tests pass, but they only cover the Python user lookup, not the page.Was Zeile 2 tatsächlich änderte, war das Markup. Allein führte sie in 5 von 10 UI-Läufen dazu, dass aria-live="polite" an das Ergebnis kam. In den übrigen 40 Läufen tat das keiner. Das Attribut ist überflüssig: Jeder Lauf benutzte ein <output>-Element, das schon implizit eine höfliche Live-Region mitbringt. „Elevate UX“ zeigte sich also als zusätzliches Accessibility-Attribut, das für einen Screenreader nichts ändert. Die Zeile brachte außerdem in 10 von 10 Läufen die Eingabemeldung, gegenüber 9 von 10 bei der Basis, was zu knapp ist, um es zu zählen.
Was passiert, wenn Vox’ AGENTS.md-Zeilen in entgegengesetzte Richtungen ziehen?
Das Ergebnis der ersten Zeile hielt in jedem gebündelten Lauf. Allein ergab Zeile 1 in 0 von 10 Läufen eine Eingabemeldung und Zeile 2 in 10 von 10, plus fünf explizite aria-live-Attribute. Kopier alle drei Zeilen zusammen hinein, wie Vox es vorschlägt, und es gibt in 10 von 10 Läufen keine Meldung und in 10 von 10 kein zusätzliches Attribut.
Die Zeilen 1 und 2 habe ich nicht als Paar ohne Zeile 3 laufen lassen, und zehn Läufe können übersehen, was ein elfter zeigen würde, das ist also ein beobachtetes Muster, kein gemessener Vorrang. Anthropics Memory-Doku warnt: „Wenn sich zwei Anweisungen widersprechen, kann Claude willkürlich eine davon wählen.“ Zehn gebündelte Läufe gingen in dieselbe Richtung, was den nächsten nicht sicher macht. An der Datei allein lässt sich nicht ablesen, welche Zeile gewinnt. Immerhin sagten alle zehn gebündelten Berichte, was bei leerer Eingabe passiert, ein NaN L oder ein leeres Ergebnis. Keiner sagte, dass zwei Zeilen der AGENTS.md gegeneinander zogen.
Verhindert Vox’ Race-Condition-Zeile in der AGENTS.md defensiven Code?
Das kann ich aus diesen Läufen nicht ablesen, aus zwei Gründen, die an meinem Design liegen, nicht an der Zeile. Der Server ist single-threaded und sagt das auch, und die Agenten ließen den Lock größtenteils ohnehin weg: 1 Lock in 10 Läufen ohne AGENTS.md, 2 von 10 mit der Basis, 0 von 10 mit allen drei Zeilen. Diese Zahlen sind zu klein, um sie auseinanderzuhalten. Fast jede Abschlussnachricht, mit oder ohne die Zeile, erklärte die mögliche Race Condition und warum sie hier harmlos ist.
Der zweite Grund ist der Print-Modus. Zeile 3 verlangt, dass der Agent erklärt und dann auf ein OK wartet, und in claude -p gibt es niemanden, auf den er warten könnte. Ein Lauf mit allen drei Zeilen nannte die Datei tatsächlich als Grund: „Gemäß AGENTS.md habe ich keinen Lock für gleichzeitige Anfragen und keine Größenbegrenzung für den Cache eingebaut“. Die Zeile änderte die genannte Begründung, nicht den Code. Ein fairer Test braucht einen Fall, in dem die richtige Antwort unklar ist, etwa einen Server mit Threads oder einen Cache, den sich mehrere Worker teilen, in einer Session, in der jemand Ja sagen kann.
Warum ändern manche AGENTS.md-Zeilen das Verhalten und andere nicht?
Weil eine Zeile ein Satz ist, den das Modell abwägt, keine Regel, die jemand durchsetzt, und die Zeilen, die im Diff auftauchen, sind die, die etwas Prüfbares nennen. Anthropics Rat sagt dasselbe: Schreib „Anweisungen, die konkret genug sind, um sie zu überprüfen“, also „Run npm test before committing“ statt „Test your changes“. In meinen Läufen brachte die Datei mit so einem Satz Sonnets Testläufe bei der Cache-Aufgabe von 0 von 5 auf 5 von 5. „Without breaking anything“ zeigte obendrauf keinen klaren Effekt.
Leute, die diese Dateien täglich benutzen, berichten dasselbe, und ihre Berichte sind einzelne Setups, keine verifizierten Ergebnisse. In einem r/codex-Thread mit der Frage, welche AGENTS.md-Zeilen überhaupt etwas geändert haben, hieß es in einer Antwort, die Zeilen, die die Ausgabe sichtbar ändern, „nennen einen Befehl, einen Pfad oder ein Ergebnis, sodass man sie im Transkript prüfen kann“. Eine andere meinte, hängen bleiben die Zeilen, die „direkt geschrieben werden, nachdem Codex einen bestimmten Fehler wiederholt, und als genau dieser Fehler formuliert sind“, etwa „new helpers go next to the file that uses them, not in utils/“ statt „keep code organized“.
Konkret allein reicht allerdings nicht. Am 16. Oktober berichtete jemand in r/codex, Codex in sieben frischen Chats über drei Modelle hinweg gebeten zu haben, denselben PR zu erklären. Jede erste Antwort ließ das Mermaid-Diagramm weg, das ihre AGENTS.md verlangte, obwohl der Agent die Regel auf Nachfrage zitieren konnte. Im späteren Thread berichtete ein Kommentator, dass Agenten vor dem Hinzufügen von „run the tests you wrote“ in 22 von 119 Läufen fertig wurden, ohne die selbst geschriebenen Tests auszuführen, und danach in 16 von 71: erst 18 Prozent, dann 23. Die Zeile, die stattdessen blieb: „An empty test run is a failure, not a pass.“
Wenn eine Zeile tragend ist, ist ein Satz nicht der richtige Behälter. Ein Prompt ist keine Invariante: Das Modell hält sich meistens daran, und ein Hook oder ein CI-Check ist das, was es jedes Mal hält.
Solltest du Vox’ Zeilen in deine AGENTS.md kopieren?
Nicht so, wie sie dastehen. Jede braucht eine Änderung.
Zeile 1 braucht, wenn deine Screens Eingaben sammeln, eine Ausnahme, die nennt, was sie nicht streichen darf: „Keep error messages, units and anything that says what a field means.“ Ohne die geht nach meiner Zählung die Meldung für falsche Eingaben verloren.
Allein in der Datei änderte die UX-Hälfte von Zeile 2 das Markup, und zwar überflüssig, und in meinen gebündelten Läufen überlebte ihr Effekt neben Zeile 1 und Zeile 3 nicht. Wenn du Accessibility willst, sag es in einer Zeile, die du prüfen kannst, etwa „announce computed results to screen readers“. Für die Hälfte mit „without breaking anything“ nenn stattdessen den Check: den Testbefehl, plus „say what the tests don’t cover“, damit ein Lauf, der nur die Seite angefasst hat, dir sagt, dass die Python-Suite darüber nichts beweist.
Zeile 3 ist vernünftig, und ich konnte sie nicht testen. Behalt sie, wenn deine Reviews immer wieder Locks finden, um die niemand gebeten hat, und prüf in einer interaktiven Session, ob der Agent dann wirklich fragt.
Behandle jede Zeile, ob von Vox oder deine eigene, als Hypothese. Ein Test kostet weniger als der Streit darüber. Zwei Aufgaben, die dein Agent wirklich erledigt, fünf Läufe mit der Zeile und fünf ohne, und für jeden Lauf ein frisches Konfigurationsverzeichnis, damit deine persönlichen Anweisungen auf keine Seite durchsickern. Unter Linux, wo der Login eine Datei ist, sieht das so aus:
set -euo pipefail
ID=$(date +%s%N); CFG=$(mktemp -d); WORK=$(mktemp -d); trap 'rm -rf "$CFG" "$WORK"' EXIT
ln -s ~/.claude/.credentials.json "$CFG/"
cp -r ./my-project/. "$WORK/"; cd "$WORK"
git init -q && git add -A && git commit -qm before
CLAUDE_CONFIG_DIR="$CFG" claude -p "<task>" --model claude-sonnet-5-5 \
--permission-mode acceptEdits --allowedTools "Read" "Edit" "Bash(npm test:*)" \
--output-format stream-json --verbose > "$OLDPWD/run-$ID.jsonl"
git add -A && git diff --cached > "$OLDPWD/run-$ID.diff"Der Symlink teilt deinen echten Login, statt ihn zu kopieren, und Plugins auf Account-Ebene laden trotzdem, also lass einmal eine Canary-Regel laufen, um zu sehen, was die Session tatsächlich liest. Dann vergleich jede Kopie des Projekts per Diff und durchsuch das Transkript nach dem Verhalten, das die Zeile auslösen soll. Zähl, bevor du die Ausgabe liest, nicht danach.
Zeigt eine Zeile in zehn Läufen keinen Effekt, dann bewirkt sie vielleicht nichts, vielleicht haben zehn Läufe es verpasst, oder deine Aufgabe hat ihr nie eine Chance gegeben, so wie hier bei Teilen von zwei der Zeilen von Vox. Such dir eine Aufgabe, bei der sie handeln müsste, bevor du sie streichst. Und wenn sie handelt, prüf, was sie sonst noch mitgenommen hat. Das ist dieselbe Wette wie bei jeder Regel, die es wert ist, in einer CLAUDE.md zu stehen: Ohne sie muss etwas kaputtgehen.
01 / Diskussion
Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.
02 / Weitere Artikel
Angriffsfläche reduzieren mit Claude Code: Anthropics drei Durchläufe auf meiner eigenen Website und eine vierte Frage, die sie nicht stellen
Anthropics Skill ist nicht veröffentlicht, die Methode lässt sich an einem Nachmittag nachbauen, und das Schwierige ist die Wahl, worauf du sie richtest. Auf meiner Website war das nützlichste Ergebnis eine kaputte Verifizierungsdatei, und die Lücke, die zählte, steckte in einer Deploy-Einstellung, die meine drei Durchläufe nie angesehen haben.
Claude menschlich schreiben lassen: Was Claude noch verrät, ist die Geschichte, die es erfindet
In meinem Test hat Claude Sonnet 5.5 Geviertstriche und Floskeln schon vermieden, bevor ich sie verboten hatte. Stattdessen hat es acht von neun Texten mit einem Vorfall aus einem Team eröffnet, das es nicht gibt, und ein Humanizer-Durchgang hat jeden dieser Vorfälle behalten, weil sie menschlich klingen.
Claude Auto Mode wird Standard in den Claude-Apps: was die Prüfung blockiert und was hinter den 89 % steckt
Anthropics Studie ist ehrlich, was ihren Aufbau angeht: müde Tester gegen einen einzigen eingeschleusten Befehl. Sie nennt keine False-Positive-Rate und keine Fehlerquote im echten Betrieb, und in Cloud-Sessions gibt es keinen Manual-Modus, zu dem man zurückwechseln könnte.