Zum Inhalt springen
Bernhard Götzendorfer
Hinter den Kulissen

Gebaut, aber nicht eingeschaltet

22 von 30 Sessions fahren null Wellen. Der Fast-Path dafür schreibt null Events und ist in 258 von 265 Läufen nicht protokolliert. Ein Diagnose-Rezept.

TL;DR

Die häufigste Krankheit in meinem eigenen Repo ist nicht der Bug. Es ist das Feature, das grün ist und nie feuert. Ein Feature ohne Ereignis pro Aktivierung ist eine Behauptung, keine Fähigkeit. In der unangenehmeren Variante ist es sogar unentscheidbar, ob es läuft: der Code existiert, ein Ereignis dazu gibt es nicht. Zwei Fälle aus den letzten Wochen zeigen das Muster. Ein Express-Path, der genau für den häufigsten Session-Typ gebaut wurde und dessen Aktivierung in 258 von rund 265 Läufen nirgends steht. Und ein zweiter Rechner, der seit Wochen bereitsteht und praktisch nicht benutzt wird. Dazu vier grep-Kommandos, mit denen du deinen eigenen Fall in ein paar Minuten findest.

Der Fall, der die Frage nicht beantworten ließ

Ich wollte etwas Harmloses wissen: wie viele meiner Sessions überhaupt Wellen fahren. Die Antwort ist eine Zeile:

tail -30 .orchestrator/metrics/sessions.jsonl \
  | jq -s '{n:length, zero_wave_sessions: ([.[]|select(((.waves//[])|length)==0)]|length)}'
# {"n":30,"zero_wave_sessions":22}

22 von 30 Sessions fahren null Wellen. Alle 22 sind Housekeeping-Läufe, und alle sieben Deep-Sessions im selben Fenster haben Wellen gefahren. Das Bild ist also nicht kaputt, es ist nur anders als der Bauplan im Kopf: der Standardfall ist die kurze Session, nicht die große.

Ein Detail an diesem Kommando ist wichtiger als das Ergebnis. waves ist ein Array, kein Zähler. Ein Filter auf .waves == 0 misst still gar nichts, er liefert null Treffer und sieht dabei aus wie eine saubere Antwort. Mein erster Versuch war genau der.

Für diesen Standardfall existiert seit Monaten ein Express-Path: eine Abkürzung, die kurze Housekeeping-Läufe an der schweren Planungsmaschinerie vorbeiführt. Er ist gebaut, er ist getestet, er steht im Skill-Text. Ob er in diesen 22 Sessions gegriffen hat, konnte ich nicht feststellen. Nicht schwer, sondern unmöglich: rückwirkend gibt es keine Spur, aus der man es rekonstruieren könnte. Das ist die Systemkrankheit dieses Repos in ihrer Beobachtungsvariante, und der Name dafür hat sogar ein Datum: gebaut, aber nicht eingeschaltet, sechs Fälle an einem einzigen Tag am 08.08.2026.

Vier Fragen, die jedes Feature beantworten muss

Aus diesem Fall ist ein Rezept geworden. Vier Fragen, vier Kommandos, jeweils mit dem Ergebnis, das ich gemessen habe:

# 1. Kennt die Konfiguration den Schlüssel überhaupt?
grep -n "express-path" CLAUDE.md
# keine Treffer

# 2. Kennt der Parser ihn?
node scripts/parse-config.mjs | jq '."express-path"'
# null

# 3. Gibt es genau ein Ereignis pro Aktivierung?
grep -c "express" .orchestrator/metrics/events.jsonl
# 0

# 4. In wie vielen Läufen ist die Aktivierung protokolliert, in wie vielen Feldformen?
grep -o '"express_path":[^,}]*' .orchestrator/metrics/sessions.jsonl | sort | uniq -c
# 14 false, 6 true, 1 abweichende Form, 258 Läufe ohne das Feld

Vier Antworten, ein Befund. Der Skill-Text behauptet, der Express-Path sei standardmäßig aktiv. Der Parser kennt den Schlüssel nicht. Es gibt kein Ereignis. Und dort, wo überhaupt ein Feld existiert, existiert es in zwei verschiedenen Formen. Zwei Feldformen sind keine Historie, sie sind zwei halbe.

Die vierte Frage ist die wichtigste, weil sie die einzige ist, die man nicht wegdiskutieren kann. Ein Feature ohne Ereignis ist eine Behauptung, keine Fähigkeit. Verifikation schlägt Vertrauen, und das gilt für die eigene Infrastruktur genauso wie für generierten Code, wie ich in Der knappe Skill ist nicht Tippen, sondern Verifikation beschrieben habe.

Wenn der Parser keine Allowlist hat, ist jedes Schweigen erlaubt

Die Ursache liegt eine Ebene tiefer und ist unspektakulär. Der Config-Parser ist 149 Zeilen ohne Schlüssel-Allowlist. Ein fehlender Schlüssel ist damit nie ein Fehler, sondern immer ein stilles Aus. Wer sich vertippt, schaltet ein Subsystem ab und bekommt kein Wort dazu. Wer einen Schlüssel nie hinschreibt, weil er in der Dokumentation als Default steht, ebenfalls.

Als ich das einmal durchgezählt habe: zwölf Subsysteme standen auf enabled: false, darunter die Dokumentations-Orchestrierung, mehrere Staleness-Prüfungen, die Wellen-Reviewer und der Reconcile-Lauf. Weitere 42 dokumentierte Top-Level-Schlüssel fehlten in meiner Projektdatei komplett und fielen still auf ihren Default. Das ist keine Sabotage, das ist Erosion: jeder einzelne Schlüssel wurde irgendwann bewusst nicht gesetzt, und in Summe lief ein anderes Werkzeug als das, das ich zu benutzen glaubte.

Die Nachher-Zahl ist der ernüchternde Teil. Nachdem ein echtes Ereignis eingebaut war, standen über 7 Repos 36 Auswertungen zur Verfügung. Aktiviert wurde der Express-Path in 2 davon, also 5,6 Prozent. Das Verdikt der eigenen Auswertung lautet folgerichtig: nicht falsifizierbar. Ein Feature messbar zu machen heißt eben nicht, dass es danach gut dasteht.

Dasselbe Muster steckt in Nachbar-Instrumenten derselben Flotte, gemessen über 18 Repos, 143.967 parsebare Events und 1.692 verschiedene Session-IDs. Ein Stop-Ereignis mit einer Dauer, die in 8.127 von 8.127 Fällen literal 0 ist. Ein Ende-Ereignis, dessen Grund zu 96,3 Prozent schlicht "other" lautet. Ein Lock mit 1.050 Acquisitions gegen 95 Releases, von denen nur 11,3 Prozent überhaupt ein Auflösungs-Ereignis tragen. Alle drei Befunde sind dieselbe Sache: Ebenen, die Kontext und Pflege kosten und deren Wirkung niemand messen kann.

Die zweite Hälfte: ein Rechner, der bereitstand und nicht benutzt wurde

Der zweite Fall ist unangenehmer, weil hier kein Parser schuld ist, sondern ich. Seit dem 27.08.2026 kann ich schwere Wellen auf einen zweiten Mac auslagern, über einen SSH-Alias, mit einem eigenen Skill dafür. Eingerichtet, dokumentiert, funktionsfähig. Meine eigene Zählung der tatsächlich ausgeführten Remote-Befehle, Stichtag 02.09.2026:

  • 27.08.: 129 Befehle bei 1 Skill-Ladung, das war der Einrichtungstag
  • 28.08.: 31 Befehle bei 7 Ladungen
  • 29.08.: 16 Befehle bei 1 Ladung
  • 30.08. bis 01.09.: 0 Befehle bei 2 Ladungen
  • 02.09. vor dem Umbau: 3 Befehle bei 5 Ladungen

Seit dem 30.08. also fünfmal geladen, null Mal angewendet. Das Skill wurde gelesen, der Host als erreichbar erkannt, und dann lief die Arbeit trotzdem lokal. Das Lastbild machte es nicht besser: auf dem Arbeitsrechner 24 Claude-Prozesse bei 47 Prozent freiem Speicherdruck, auf dem Zielrechner 9 bis 11 Prozesse, 95 Prozent frei und eine Load um 2. Ohne eine Klammer für Prozessprioritäten stand die 1-Minuten-Load auf dem Arbeitsrechner schon während eines einzigen Quality-Gates bei 98 auf 18 Kernen.

Die Sessions lasen "erreichbar" und planten trotzdem lokal.

Vier Ursachen, keine davon Faulheit

Als ich mir die Nichtnutzung angesehen habe, kamen vier Gründe heraus, und keiner davon lautet "keine Lust".

Erstens die Handarbeit. Eine Auslagerung waren zehn Handgriffe: Erreichbarkeit prüfen, Worktree vorbereiten, Prompt bauen, Lauf starten, Patch holen, Patch prüfen, einspielen, aufräumen, Ergebnis verbuchen, Fehlerfall abfangen. Zehn Handgriffe verlieren jedes Rennen gegen einen lokalen Agentenstart.

Zweitens die kaputte Authentifizierung für den Betrieb ohne Fenster. Die Zugangsdaten lagen im macOS-Schlüsselbund, der für SSH-Sitzungen gesperrt ist. Der dateibasierte Zugang, der genau diesen Fall abgedeckt hätte, war am selben Tag beim Aufräumen mit gelöscht worden.

Drittens fehlte ein mechanischer Auslöser. Das Gate, das bei Ressourcenknappheit die Welle verkleinert, kannte gar keinen zweiten Host. Es konnte also nie vorschlagen, was ohnehin bereitstand. Inzwischen ist die Regel umgedreht: das Gate probt nie selbst das Netz, aber jede Entscheidung schreibt ein Ereignis, bei jedem Versuch und bei jeder Verweigerung. Ohne Zeugen bleibt die Entscheidung lokal. Der Payload trägt dabei den konfigurierten Alias, nie einen Hostnamen und nie eine Adresse.

Viertens die Hinterlassenschaften. Ein früherer Auslagerungs-Versuch lief im Haupt-Checkout statt in einem eigenen Worktree und ließ 67 geänderte Dateien und einen Sync-Branch zurück. Wer einmal so aufräumen musste, greift beim nächsten Mal zögerlicher zu.

Und jetzt der Teil, den ich mir nicht schönreden kann: Ich habe keinen Benchmark. Es gibt kein Vorher und kein Nachher, keine gemessene Zeit- und keine gemessene RAM-Ersparnis durch das Auslagern. Der Ertrag dieser Übung ist nicht der Gewinn, sondern der Audit selbst. Ich weiß jetzt, dass ein eingerichtetes Werkzeug wochenlang unbenutzt danebenliegen kann, während ich es in Gesprächen als vorhandene Fähigkeit erwähne. Das ist der Befund. Alles andere wäre erfunden.

Die Konsequenz steht inzwischen auch im Skill. Exit-Codes von 0 bis 8, und ein leerer Patch zählt ausdrücklich als Fehlschlag und nicht als Erfolg. Fünf Rollen wandern grundsätzlich nie auf einen anderen Rechner: Kern-Implementierung, Security-Review, Migrationen, Releases und alles, was Secrets anfasst. Und wer den Gate-Log oder den Patch nicht liest, hat kein Ergebnis: erledigt und korrekt sind zwei verschiedene Behauptungen.

Was ich daraus mitnehme

Alle vier grep-Kommandos laufen auf dieselbe Frage hinaus: wird pro Aktivierung genau ein Ereignis geschrieben? Wird es das nicht, ist die Frage nach der Wirkung nicht schwer, sondern unbeantwortbar, und keine noch so gründliche Codelektüre schließt diese Lücke.

Zwei Detailbefunde erklären, wie so etwas monatelang unbemerkt bleibt. 149 Zeilen Parser ohne Schlüssel-Prüfung haben zwölf Subsysteme abgeschaltet, ohne ein einziges Wort dazu, weil ein Tippfehler dort still zu einem Aus wird. Und zwei Feldformen sind keine Historie: 14 mal false, 6 mal true, eine abweichende Form und 258 Läufe ohne das Feld ergeben zusammen keine Auswertung, sondern zwei halbe.

Der zweite Fall, der ungenutzte Rechner, hat einen anderen Grund, aber dieselbe Form. Wo kein mechanischer Auslöser sitzt, entscheidet Zeitdruck, und Zeitdruck wählt immer den lokalen Weg, egal wie gut das andere Werkzeug dokumentiert ist. Deshalb ist dieser Nichtnutzungs-Audit billiger als das nächste Feature: vier grep-Kommandos gegen mehrere Tage Bauzeit für etwas, das danach auch niemand einschaltet.

Fazit

Ich zähle inzwischen zuerst die Aktivierungen und dann die Zeilen. 73 Prozent meiner Sessions sind Housekeeping, 21 Repos auf diesem Rechner tragen den Orchestrator-Ordner, und in fast allen davon steht mehr Fähigkeit im Text als in den Daten. Eine Prüfung, die unverdrahtete Konfigurationsschlüssel findet, gab es schon vorher. Sie findet nur keine unbeobachtbaren Phasen, und genau die sind das Problem.

Wenn du selbst an einem größeren Agenten-Setup baust: nimm die vier Fragen von oben und stell sie deinem liebsten Feature. Wie das Verfahren aussieht, in dem das bei mir hängt, steht in Wie session-orchestrator als OSS-Tool in 9 Tagen reift und in Eine Agenten-Flotte: Sven, BuilderBob und ihr Loop. Wie ich arbeite, steht unter Arbeitsweise, und wenn du eine ähnliche Baustelle hast, schreib mir.