Pixel — ein LLM als Co-Maintainer in Production
Mai 2026 · puzzlekreis.de · FastAPI + Next.js + React Native
puzzlekreis.de ist eine Tauschbörse für Puzzles. Backend in Python (FastAPI, async SQLAlchemy, Postgres 16, Redis 7), Web in Next.js 14 als PWA, Mobile in React Native (Android und iOS via EAS). Knapp 150 Backend-Files, 30+ Pytest-Suiten, ~14k TS/TSX im Frontend, deployed auf einem Hetzner-CX33. Es gibt zwei Maintainer: mich, und einen Agent namens Pixel.
Pixel arbeitet nicht in Tickets. Pixel arbeitet in Branches. Wenn Pixel einen Run abschliesst, ist das Resultat keine Empfehlung, kein Diff in der Chat-Antwort, sondern ein feat/...-Branch mit Tests und einem Commit auf origin. Ich ziehe ihn morgens, lese durch, mergen oder zurückgeben mit Anmerkungen.
Dieser Text beschreibt, wie das funktioniert, wie ich es gegen offensichtliche Fail-Modes abgesichert habe, und warum es nicht der Endgegner ist, an den ich Branches einfach abnicke.
Die Loop
Pixels Runner ist Geschwister-Skript zu dem Bug-Bounty-Runner, den ich an anderer Stelle beschreibe — gleicher Bash, andere Verarbeitung. Pro Run gibt es:
- eine
queue.txtmit Aufgaben: V2: Analytics-Event-Hooks instrumentieren · V3: Listing-Editor erweitern · V4: … - einen Prompt pro Aufgabe, der Kontext liefert: was die Aufgabe ist, welche Files relevant sind, welche Tests bestehen müssen, und — wichtig — den Hinweis auf Convention-Files im Repo, die der Agent vor dem Schreiben lesen soll
- eine
git for-each-ref-Snapshot vor jedem Run, um neue Branches zu detektieren
Pixel öffnet einen frischen Claude-CLI-Prozess, bekommt den Prompt, und arbeitet. --add-dir zeigt auf das Repo. Der Agent darf alles im Repo, was nicht main committet. Am Ende soll er einen feat/...- oder fix/...-Branch erstellt, Tests grün gefahren, und gepusht haben.
Was Pixel an einem Tag tut
Run 6, Task V2, abgeschlossen am 1. Mai: Analytics-Event-Hooks instrumentieren. Der Prompt: "Instrumentiere alle Backend-Service-Methoden mit Aufrufen an die analytics.py-V1, die noch nicht in main ist; nutze fail-soft try-imports, damit fehlende V1 keine Tests bricht; lege eine Test-Suite an."
Was Pixel produziert hat:
Branch: feat/analytics-event-hooks (from main, commit 34953fa)
Files modified:
backend/app/services/auth.py
backend/app/services/listing.py
backend/app/services/listing_photo.py
backend/app/services/message.py
backend/app/services/rating.py
backend/app/services/swap.py
backend/app/services/user_account.py
Files created:
backend/tests/test_analytics_hooks.py
17 hook sites covering 18 distinct event types:
signup, login, logout
listing_create, listing_publish, listing_unpublish, listing_view
photo_upload
swap_request, swap_accept, swap_decline, swap_cancel,
swap_ship, swap_complete
message_sent
account_delete, account_restore
rating_given
Das war eine Aufgabe, die für mich vermutlich 4-5 Stunden mit Pausen gedauert hätte. Pixel hat 38 Minuten gebraucht. Ich habe den Branch am Morgen gelesen, drei Anmerkungen zurückgespielt (Naming einer Variable, fehlender Edge-Case in einem Test, eine kleine API-Konsistenz-Frage), Pixel hat in der zweiten Run-Iteration die Anmerkungen umgesetzt. Merge.
Über 5 abgeschlossene Runs (Run 2-6) hat Pixel ungefähr 20 solche Tasks erledigt — Analytics-Hooks, Photo-Gallery-Lightbox, Brand-Migration, FTS-Migration mit Postgres-array-IMMUTABLE-Wrapper, Sentry-CSP-Konfiguration, eine Reihe Edge-Bugs vor dem Soft-Launch.
Was ich absichern musste
Branch-Detection statt Status-File. Beim Bug-Bounty-Runner ist die Erfolgs-Schnittstelle eine status.json. Bei Pixel ist sie ein neuer Branch auf origin. Die Sentinel-Recovery ist entsprechend:
# Vor dem Run: Snapshot aller Branches
branches_before=$(cd "$REPO" && git for-each-ref --format='%(refname)' refs/heads refs/remotes/origin)
# … claude --print … läuft …
# Nach dem Run, falls der Agent kein Status-File geschrieben hat:
branches_after=$(cd "$REPO" && git for-each-ref --format='%(refname)' refs/heads refs/remotes/origin)
new_branches=$(comm -13 <(echo "$branches_before") <(echo "$branches_after") | \
grep -E 'feat/|fix/|chore/|test/' | head -3)
if [ -n "$new_branches" ]; then
cat > "$STATUS_DIR/$next_id.status" <
Wenn der Agent abbricht ohne Status, aber einen Branch gepusht hat: das ist Erfolg. Wenn er weder noch produziert hat: das ist Failure. Wenn er einen Branch lokal hat, aber nicht gepusht: das ist mein Bug, weil ich vergessen hatte, das Push im Run-Workflow zu erzwingen.
Token-Telemetrie persistent. Jeder Run schreibt eine Zeile in usage-tracker.json, gegliedert nach UTC-Tag: input-Tokens, Output-Tokens, Modell, Task-ID. Approximiert (Wörter × 1,3) reicht das für Tagessummen, die ich in einem kleinen Telegram-Bot abrufen kann. Damit weiss ich, was ein "feat/analytics-hooks" mich kostet — typischerweise zwischen 0,8 und 2 Mio Tokens, je nach Repo-Read-Aufwand.
Kein git push --force. Pixel darf git push origin feat/..., aber kein force-push. Wenn ein Branch existiert und auseinandergelaufen ist, bricht der Push, der Run wird als blocked gemarkt, ich räume das manuell auf. Ich habe das gelernt, als Pixel in einem frühen Run einen lokalen Branch gegen den Remote-Branch unterschiedlich gerebased hatte und den Unterschied via Force-Push wegmoviert hätte. Seitdem: Force-Push ist Privileg, nicht Default.
Convention-Files lesen ist Pflicht. Im Repo liegen backend/CLAUDE.md, web/CLAUDE.md, mobile/CLAUDE.md mit Konventionen — Migration-Patterns, Test-Strukturen, Pre-Commit-Hooks. Im Prompt sage ich Pixel explizit: "Lies zuerst die CLAUDE.md-Files in den Subdirs, die du anfasst, und folge denen." Das hat den Anteil von "Pixel hat eine eigene Mini-Migration-Konvention erfunden"-Branches auf nahe null gedrückt.
Wo Pixel scheitert
Architekturentscheidungen. Wenn der Prompt eine Aufgabe enthält wie "entscheide, ob Feature X in Backend oder Frontend gehört, und implementiere", bekommt Pixel das oft falsch. Der Agent neigt dazu, zu vorhandenen Patterns zu konvergieren, auch wenn ein neuer Pattern besser wäre. Das ist okay — solche Entscheidungen treffe ich vor dem Prompt, und der Prompt enthält dann die Vorgabe.
Cross-cutting Performance. Pixel kann eine N+1-Query in einer einzelnen Methode fixen, wenn ich darauf zeige. Pixel kann nicht auf den Hund kommen, dass ein neuer Endpoint in der Latenz-Statistik durch die Decke geht, weil eine andere Methode ein Lock hält. Performance-Profile sind ausserhalb des Sichtfelds.
"Eigentlich-doch"-Refactorings. Pixel folgt der Aufgabenstellung sehr wörtlich. Wenn ich "instrumentiere die 17 Hook-Sites" sage, instrumentiert Pixel 17 Hook-Sites. Pixel sagt nicht: "Petrit, drei dieser Hooks gehören eigentlich in einen gemeinsamen Decorator, soll ich das umbauen?" Solche Vorschläge mache ich selber, oder lasse sie durch. Ich verliere damit etwas Code-Hygiene; ich gewinne damit Reproduzierbarkeit.
Warum das nicht der Endgegner ist
Ich werde häufig gefragt, ob das System "den Maintainer ersetzt". Die Antwort ist nein, aber sie ist auch nicht "der Mensch ist immer noch klüger". Die Antwort ist: der Engpass verschiebt sich.
Vor Pixel war mein Engpass die Tatsache, dass ich nach der Tagesarbeit zu müde war, das nächste Feature anzufangen. Mit Pixel ist mein Engpass das Schreiben präziser Aufgaben-Prompts und das Reviewen der Branches. Letzteres ist Arbeit, die mir liegt — kuratieren, Anmerkungen geben, Konventionen verfeinern. Ersteres ist eine Fähigkeit, die ich vor Pixel nicht hatte: ich musste lernen, einen Task so zu formulieren, dass ein Agent ihn ohne mich erledigen kann. Diese Übung hat meine Aufgaben für menschliche Mitarbeiter:innen gleich mit verbessert.
puzzlekreis.de wäre ohne Pixel an einem Drittel der heutigen Featuredichte. Mit Pixel ist es eine kleine Webapp mit zwei Maintainern, von denen einer 24/7 verfügbar und 1000× billiger ist als die Stunde Mensch — und der andere immer noch derjenige, der entscheidet, was wert ist gebaut zu werden.
puzzlekreis.de geht in den nächsten Wochen in Soft-Launch. Wenn dich das Setup interessiert (oder du selber Puzzles tauschen willst), schreib mir.