31 Lessons in 5 Tagen: Warum die wertvollste Datei im Repo die über das Scheitern ist
Teil 5 einer Artikelserie über zwei eigene Agenten-Projekte: wintermute (produktiver persönlicher KI-Agent) und agenticframework (produktiv eingesetztes Meta-Framework für autonome Entwicklungs-Pipelines).
Die ehrlichste Datei im Repository
In wintermutes Repo liegt eine Datei namens LESSONS.md. Sie enthält 31 nummerierte Einträge, und die ersten stammen aus einem Fenster von gerade einmal fünf Tagen Produktivbetrieb. Man kann diese Zahl auf zwei Arten lesen: "Da ist ja ständig etwas kaputtgegangen" — stimmt. Oder: "Da wurde fünf Tage lang nichts unter den Teppich gekehrt" — stimmt auch, und das ist die interessantere Lesart. Denn in dieser Serie habe ich die Datei inzwischen in jedem einzelnen Teil zitiert: Sie ist die Quelle der Halluzinations-Vorfälle, der Updater-Architektur, der Ticket-Sizing-Regeln. Diese Artikelserie existiert nur, weil das Lessons-Log existiert.
Deshalb zum Abschluss der technischen Kernthemen ein Artikel über das Instrument selbst: Wie man Produktionsfehler so dokumentiert, dass sie nicht Archiv werden, sondern Betriebssystem.
Ein Lessons-Log braucht eine Leseanleitung
Das Format beginnt mit etwas, das die meisten Postmortem-Sammlungen nicht haben: einer Anleitung, wie man die Datei liest — inklusive einer Konvention für überholtes Wissen:
"Some entries open with a
> ⚠️ DISCONTINUEDblockquote. Those document approaches we used to take and have since replaced. They are kept on purpose — the historical context explains why the current architecture looks the way it does — but the discontinued approach should not be reintroduced without re-reading the entry that replaced it. The marker tells you at a glance: 'this was a stepping stone, not the destination.'"
Das löst das klassische Dilemma jeder Doku: Löscht man überholte Einträge, verliert man das Warum der heutigen Architektur. Behält man sie unmarkiert, führt man Leser in die Irre. Die dritte Option — behalten, aber explizit als Zwischenschritt markieren — ist trivial umzusetzen und trotzdem selten.
Anatomie einer guten Lesson: ein Symptom, drei Ursachen
Die Muster-Lesson des Logs ist #8, ein kleiner Debugging-Krimi. Das Symptom: ein leerer Fehler-Umschlag, {"error": ""} — dieselbe Anzeige, drei völlig verschiedene Geschichten dahinter. Ursache eins: Der Container wurde während eines Self-Updates neu gebaut, die HTTP-Antwort ging verloren — das Update war erfolgreich, nur die Erfolgsmeldung starb. Ursache zwei, Tage später, gleiches Symptom, kein Rebuild weit und breit: Ein langer Embedding-Lauf (27 sequenzielle Ollama-Aufrufe, ~50 Sekunden) überschritt den 30-Sekunden-Timeout des HTTP-Clients — die Arbeit wurde vollständig erledigt, aber str(httpx.ReadTimeout) ist ausgerechnet der leere String. Ursache drei: ein tatsächlicher Fehler beim Laden.
Die Lesson dokumentiert nicht nur die Varianten, sondern den Reflex:
"Der falsche Reflex bei einem leeren Error-Envelope ist Retry. Der richtige Reflex ist Effekt am Gateway-State prüfen, dann entscheiden ob die Operation noch nachgefahren werden muss. [...] bei nicht-idempotenten Tools kann ein Retry doppelte Side-Effects erzeugen."
Das ist das Format-Geheimnis: Problem, unterscheidbare Varianten mit Datum, Diagnose-Checkliste, und am Ende ein Reflex — keine Moral, sondern eine Handlungsanweisung für das nächste Mal.
Der Unterschied zwischen Friedhof und Betriebssystem
Der eigentliche Test einer Fehlerkultur ist nicht, ob dokumentiert wird — sondern ob die Dokumente zurückwirken. Ein Grep über wintermutes Code findet elf explizite Lesson-Referenzen in Code, Tests und Konfiguration. Zwei Beispiele:
# tests/updater/test_auth.py
# Lesson 16 paranoia: a stray paste-mangled space should not unlock the updater.
# agent/src/agent/config.py — Begründung des Tool-Timeouts:
# the previous 30s was too tight: see Lesson 8 variant 2, Issue #35,
# and the 2026-05-06 11:10 incident...
Die Lesson über den verstümmelten Collection-Namen (Teil 1 dieser Serie) lebt als Testkommentar weiter, der begründet, warum dieser Test existiert. Der Timeout, der Lesson #8 auslöste, trägt seine eigene Herleitung als Docstring — der nächste Entwickler, der "warum eigentlich 300 Sekunden?" fragt, bekommt keinen Magic Number, sondern einen Verweis auf den Vorfall mit Datum und Uhrzeit. Lessons, die niemand mehr anfasst, sind ein Friedhof. Lessons, auf die Tests und Timeouts zeigen, sind Architektur-Begründung in ausführbarer Nähe.
Lessons schreiben für das Wiederfinden
Meine Lieblings-Lesson ist die Meta-Lesson: #28 dokumentiert, dass das semantische Wiederfinden von Lessons ein eigenes Handwerk ist. Der Befund: Eine Suche nach dem Symptom ("leerer Error-Envelope nach langem Tool-Call") findet die richtigen Lessons zuverlässig; eine Suche nach dem vermuteten Mechanismus ("httpx ReadTimeout") findet nichts — obwohl die Lesson den Mechanismus enthält:
"The mechanism vocabulary is what you bring to the recall; the lessons store the symptoms because that's what was seen first."
Daraus folgt eine Schreibregel: Jede Bug-Lesson braucht beide Formulierungen — einen Absatz in Symptom-Sprache (was jemand sieht, der das Problem hat) und einen in Mechanismus-Sprache (was jemand sucht, der es debuggt). Zwei Absätze statt einem, damit die Suche von beiden Seiten trifft. Wer je eine Wissensdatenbank aufgebaut hat, die niemand durchsucht gefunden hat, weiß, wie viel diese eine Regel wert ist.
Von der Lesson zur Regel: die agenticframework-Variante
agenticframework führt dieselbe Praxis eine Stufe formaler. Dort ist der dokumentierte Weg: Lesson → Issue → durchgesetzte Regel. Die Ticket-Sizing-Lektion aus Teil 4 wurde zur Spezifikation mit drei Kontrollpunkten. Und Lesson 2 zeigt die Miniatur-Version desselben Musters: Im Review fiel ein Schema-Feld auf (DiffValidationResult.warnings), das als Reserve für die Zukunft angelegt war — validiert, getestet, und von keiner einzigen Codezeile je befüllt. Die daraus destillierte Regel:
"Do not add a field to a schema unless at least one code path in the same PR produces a non-trivial value for it. A field that is always empty [...] is reserve surface, not implemented surface."
Begründet mit der vollen Kostenrechnung: Jeder Konsument behandelt das Feld defensiv, künftige Leser vermuten Bedeutung, wo keine ist, und Tests, die warnings == [] prüfen, sind Rauschen statt Signal. Das ist Fehlerkultur im engsten Sinne — hier ist nicht einmal etwas kaputtgegangen. Es wurde eine zukünftige Verwirrung dokumentiert und wegregiert, bevor sie Zinsen ansetzen konnte.
Der ehrliche Teil: Was diese Kultur kostet
Erstens: Schreibzeit, konsequent auch dann, wenn gerade alles brennt — die 31 Einträge in fünf Tagen bedeuten, dass mitten im dichtesten Incident-Fenster zusätzlich dokumentiert wurde. Zweitens, und das ist die pikante Pointe: Selbst das Lessons-System selbst produzierte eine Lesson. Eintrag #29 dokumentiert, wie die Konversations-Historie an ### -Trennern zerschnitten wurde — bis Antworten auftauchten, die selbst ### Summary-Überschriften enthielten, womit jede davon als Nachrichtengrenze interpretiert wurde und das Modell fortan verstümmelte Kopien seiner eigenen Beiträge zu lesen bekam. Die Regel daraus ("Parse on an anchored, unambiguous delimiter, not a substring that the payload can contain") ist Lehrbuchstoff. Dass sie im eigenen Gedächtnissystem gelernt werden musste, ist die Erinnerung daran, dass Fehlerkultur kein Zustand ist, sondern ein Betriebsmodus — auch für die Werkzeuge, mit denen man sie betreibt.
Fazit und Ausblick
Ein gutes Lessons-Log hat vier Eigenschaften: eine Leseanleitung mit Umgang für überholtes Wissen, ein festes Format mit Reflex statt Moral am Ende, Rückverweise aus Code und Tests, und eine Schreibdisziplin, die das Wiederfinden mitdenkt. Nichts davon ist technisch anspruchsvoll. Alles davon ist Disziplin — und genau deshalb ist es seltener als jede Technologie.
Im nächsten Teil geht es um die Grenze, die auch die beste Fehlerkultur nicht verschieben kann: Wann darf ein Agent nicht mehr selbst entscheiden? Es wird um die Abschaltung der Self-Modification gehen — diesmal nicht als Anekdote, sondern als Architekturprinzip: Eskalation als First-Class-Ergebnis.
Quellen: wintermute/LESSONS.md (Leseanleitung Zeilen 1–23, Lesson #8 Zeilen 200–278, Lesson #28 Zeilen 887–942, Lesson #29 Zeilen 945–966, Lesson #18 Zeilen 444–470), Lesson-Referenzen in tests/updater/test_auth.py:43, tests/gateway/test_config.py, agent/src/agent/config.py:62, agent/src/agent/tools.py, updater/src/wintermute_updater/actions.py:207, docs/TROUBLESHOOTING.md; agenticframework/LESSONS.md (Präambel, Lesson 1 → Issue #41, Lesson 2 / Issue #60 Zeilen 75–105).