Televism
← Alle Beiträge

Die Dark Factory

Die Zusammenarbeit mit meinem Agenten läuft rund – und trotzdem fühle ich mich blockiert, weil ich bei der Umsetzung nur noch zusehe. Wie aus dem Wunsch nach mehr Effizienz eine Fabrik wurde, die am Merge Request ihre eigene Geschichte erzählt.

Zuschauer am eigenen Rechner

Inzwischen läuft die Zusammenarbeit mit meinem Agenten richtig gut. Der Ablauf hat sich eingespielt:

  1. Claude und ich diskutieren eine Idee und formulieren den Plan.
  2. Er setzt ihn um.
  3. Ich pushe das Ganze in einen Merge Request.
  4. Ich lasse Claude ein Review machen und, wenn nötig, gleich fixen – und ja, es bringt wirklich etwas, wenn Claude Claude reviewt.
  5. Merge.

Ein gutes Vorgehen. Nur: Bei Schritt 1 trage ich aktiv bei. Beim Rest bin ich Zuschauer am eigenen Rechner. Die Umsetzung dauert manchmal 30 Minuten und länger; ich warte, um ab und zu ein paar Klicks zu machen – und je besser wir uns als Team einspielen, desto weniger Fragen muss ich nach der Planung überhaupt noch beantworten. Auch die Schritte nach der Implementierung sind stumpf: warten auf die Pipeline, Claude reviewen und fixen lassen, wieder auf die Pipeline warten – gähn. Die spannenden Schritte – die Idee, das Aushandeln des Ansatzes – will ich behalten. Das stumpfe Danebensitzen darf gern weg.

Also die naheliegende Frage: Kann das „Machen” nicht woanders laufen, während ich mich um die nächste Idee kümmere?

Evolution der Idee

Man hätte das mit Claude in der Cloud lösen können, und grundsätzlich lassen sich auch einfach mehrere Sessions lokal starten. Aber bei uns in der Firma ist gerade Dark Factory ein Thema – die Halle, in der die Maschinen auch dann weiterlaufen, wenn niemand das Licht anmacht – und ich hatte schlicht Bock, etwas in diese Richtung zu bauen.

Ich ging davon aus, dafür irgendeinen AI-Server zu brauchen, und fing schon an, nach n8n-Hosting zu suchen. Clauds Empfehlung war viel simpler: Wir haben doch längst eine Fabrikhalle. Sie heißt GitLab CI.

Der Übergabepunkt bleibt der kreative: Aus der Diskussion entsteht ein GitLab-Work-Item mit einem Umsetzungsplan. Ab da übernimmt die Halle – als ganz gewöhnliche Pipeline-Jobs:

  • implement nimmt den Plan aus dem Ticket, lässt Claude umsetzen, committet, pusht einen Branch und öffnet den Merge Request. Damit der Umsetzer nicht nur das Wie, sondern auch das Warum kennt, lädt der Job zum Plan des Tasks zusätzlich die Spec der Eltern-Story.
  • review springt an, sobald die Tests grün sind, und hängt seine Findings an den MR.
Mensch implement (Job) Merge Request Build / Test review von Hand ODER automatisiert die normale MR-Pipeline – unverändert
Ob der MR von Hand entsteht oder vom implement-Job: ab dem Merge Request läuft die ganz normale Pipeline – unverändert.

Am Ende steht ein MR, den ein Mensch abnimmt. Das Zusehen beim Tippen ist weg; das Urteil bleibt.

Evolution der Umsetzung

Der Einstieg war erstaunlich einfach:

  • ein Image mit Claude-CLI und den Entwicklungswerkzeugen (Maven, Node, …)
  • ein neuer Job
  • ein cleverer Prompt
  • ein paar Secrets in GitLab – Claude bietet glücklicherweise, anders als Codex, die Möglichkeit, ein festes Token für einen Job zu erzeugen, um sein Kontingent auch in der Cloud zu nutzen

Fertig!

Der Review-Job unterscheidet sich im Wesentlichen nur durch den Prompt. Aber auch wenn Claude, der Claude reviewt, brauchbare Findings liefert, wollte ich ein anderes Modell einsetzen, um blinde Flecken zu verkleinern. Also opencode ins Image, einen DeepSeek-Account angelegt, 20 $ eingezahlt – und seither reviewt DeepSeek. Ein exzessives A/B-Testing der beiden Modelle habe ich nicht gemacht. Quantitativ findet DeepSeek mehr Findings als Claude zuvor; Claude als Implementer akzeptiert sie meistens. Qualitativ sind es in der Regel Kleinigkeiten. Oft genug findet das Review aber auch größere Sachen (klappt unter dieser und jener Bedingung nicht; dieser Datenbank-Zugriff resultiert unnötig in N+1 Abfragen), sodass ich es auf jeden Fall behalten will.

Läuft!

Auf die technischen Probleme will ich nicht im Detail eingehen, aber davon gab es auch einige:

  • 30 Minuten implement saugen das 400-Minuten-Kontingent von GitLab schnell leer → ein eigener Runner musste her.
  • Learning: Was man deterministisch machen kann, sollte man auch deterministisch machen. Statt im Prompt „Guck in den MR, implementiere das, committe, pushe, erzeuge einen MR” lieber: Ein Skript lädt den Task und baut daraus den Prompt, Claude setzt um, das Skript committet, pusht und erzeugt den MR → weniger Überraschungen.
  • Claudes interne Gedanken hinterher zu sehen – oder noch besser, während er arbeitet – ist ausgesprochen hilfreich. Warum hat er für diesen kleinen Task so lange gebraucht? Programmiert er noch, oder hängt er schon?
  • Im Verlauf wurde das Handling rund um den Agent-Call so komplex, dass ich von Bash auf Node umgestiegen bin.

Geschichtenschreiber

Eine Sache hat mich aber noch konzeptionell gestört: Das Fixen der Review-Findings habe ich immer noch lokal gemacht. Inzwischen hatte ich dafür einen eigenen Skill, dem ich nur sage: „Guck mal in den MR, gib eine Einschätzung, was gemacht werden sollte, frag nach dem Go und fixe das auf einem eigenen Working Tree, committe und pushe.” Ich will die Kontrolle darüber behalten, was gefixt wird und wie viele Feedback-Schleifen wir drehen. Wenn auch dieser Teil in die Cloud soll, brauchen wir einen Kommunikationskanal.

Das Gute: Wir mussten dafür nichts Neues erfinden. MRs sind ja genau dafür gedacht, dass Implementer und Reviewer miteinander kommunizieren – ob das Menschen oder Agenten sind, spielt keine Rolle. Die nötige Umstellung war klein: Wo der Reviewer sein Review vorher in einen großen Kommentar geschrieben hat, macht er jetzt pro Finding einen eigenen Thread auf. Ich schreibe an die Threads, ob das gefixt oder ignoriert werden soll, und gebe gegebenenfalls weitere Anmerkungen mit. Kommentiere ich nichts, heißt das per Konvention: Im Fix-Job wird entschieden, ob es relevant ist oder nicht. Was im Fix passiert ist, wird an den Thread gehängt.

Der Fixer ist im Prinzip der Implementer mit angepasstem Prompt. Ursprünglich wollte ich sogar dieselbe Session wiederverwenden, aber das hätte die Sache mit der Persistenz in der Pipeline unnötig kompliziert gemacht. Stattdessen erzeugt der Implementer ein Übergabe-File, in dem er zusammenfasst, was er gemacht hat und warum – und vor allem, wenn es Probleme gab, die ihn davon abgehalten haben, dem Plan zu folgen. Das kommt als erster Kommentar an den MR.

Mensch implement (Job) Merge Request Build / Test review Mensch + Job Fix oder Merge? Mensch merged Fix Mensch oder Job von Hand ODER Job Merge Fix + Commit die vertraute MR-Strecke
Der komplette Build: Nur der Einstieg (implement) und der Fix kommen hinzu – dazwischen läuft die bekannte MR-Pipeline. Nach dem Review heißt es mergen oder fixen; ein Fix ist bloß ein weiterer Commit.

Diese Informationen bekommt nun auch der Reviewer. So hat er mehr Kontext zur Umsetzung, kann sein Review entsprechend verbessern und zählt nicht mehr bei jedem Lauf aufs Neue die Findings auf, die ich längst verworfen habe.

Gar nicht so dunkel

Das Schönste ist etwas, das ich gar nicht geplant hatte. Weil alle Prozesse über den MR reden, entsteht dort ganz von selbst eine wunderbar nachvollziehbare Geschichte: oben die Beschreibung, was umgesetzt wurde, mit Umsetzungsnotizen. Darunter die Threads des fremden Reviewers, jeder ein sauber abgegrenzter Befund. Daneben, wo ich es ausgelöst habe, die Antworten des Fix-Laufs. Wer den MR öffnet, liest nicht das Ergebnis einer Blackbox, sondern kann Schritt für Schritt verfolgen, wie aus einem Ticket dieser Code wurde.

Zuletzt habe ich den Implement-Job angewiesen, bei UI-Umsetzungen Screenshots der relevanten UI-Parts an den MR zu hängen. So kann ich gleich prüfen, ob mir das Ergebnis gefällt – oder per Review-Kommentar neue Regieanweisungen an den Fixer schicken.

Mein Ziel war es, eine Halle zu bauen, in der die Maschinen weiterlaufen, wenn ich das Licht ausmache. Herausgekommen ist eine, die mir bei Tageslicht eine Geschichte über die Umsetzung erzählt, die viel detaillierter und farbenfroher ist als vieles, was ich in den manuellen Werkbänken gesehen habe.

Ein Blick in die Halle

Wie so eine Geschichte aussieht, zeigen drei Ausschnitte aus einem echten MR – die Zeitsicht im Programmplaner, von implement umgesetzt, von DeepSeek reviewt, von mir gemerged. Ein Klick aufs Bild öffnet es in voller Größe.

Kopf des gemergten Merge Requests zu Issue 53, „Zeitsicht im Programmplaner“: darunter der Hinweis auf das umgesetzte Work Item und ein vom Job angehängter Screenshot des Programmplaners mit eingeblendetem, farbig gestapeltem Zuschauer-Gebirge je Zielgruppe.
Oben der MR, darunter gleich der Screenshot, den der implement-Job von der neuen UI angehängt hat – noch bevor ich eine Zeile Code gelesen habe, sehe ich, ob mir das Ergebnis gefällt.
Die Umsetzungsnotizen des Implementers mit den Abschnitten „Was umgesetzt ist“, „Entscheidungen mit Begründung“, „Fixtures/Abnahme“ und „Verworfen / Sackgassen“.
Das Übergabe-File als Umsetzungsnotiz: was umgesetzt ist, welche Entscheidungen mit welcher Begründung gefallen sind – und welche Wege verworfen wurden.
Das unabhängige Review von DeepSeek über opencode: eine Zusammenfassung mit drei neuen Findings, darüber je ein eigener, inzwischen aufgelöster Thread pro Finding. Im aufgeklappten Thread zum fehlenden Gebirge-Test antwortet der Fix-Lauf mit „Gefixt in 8a600f3e“ und beschreibt, was er geändert hat.
Das Review des fremden Modells, nachdem der Fix-Job durch war: pro Finding ein Thread, und am Fix die Anmerkung des Implementers samt Commit, in dem er es behoben hat.

Dieses „Gefixt in …“ hat bei mir eine Erinnerung geweckt. Als ich früher in der Pharmaindustrie gearbeitet habe und wir auf Git, Gitflow und Pull Requests umgestiegen sind – das ist gar nicht so lange her, wie man denken mag –, hatten unsere Prozessverantwortlichen einen Wunsch: Der Nachvollziehbarkeit halber sollte an jedem Fix einer Review-Anmerkung stehen, in welchem Commit er behoben wurde. Wir konnten das damals als technisch nicht umsetzbar und manuell viel zu pflegeintensiv wegdiskutieren. Hier habe ich es geschenkt bekommen, ohne danach zu fragen.