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 in Branches statt in Tickets. Wenn ein Run durch ist, liegt auf origin ein feat/...-Branch mit Tests — kein Diff in einer Chat-Antwort, den ich von Hand übertragen müsste. Ich ziehe den Branch morgens, lese durch, merge oder gebe ihn mit Anmerkungen zurück. Branches blind abnicken gehört nicht zum Setup.

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:

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. Solche Entscheidungen treffe ich deshalb vor dem Prompt; 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. Aber dass ein neuer Endpoint in der Latenz-Statistik durch die Decke geht, weil eine ganz andere Methode ein Lock hält — darauf kommt Pixel nicht. Performance-Profile liegen außerhalb 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. Der Einwand "drei dieser Hooks gehören eigentlich in einen gemeinsamen Decorator, soll ich das umbauen?" kommt nicht von allein. Solche Vorschläge mache ich selber. Das kostet ab und zu Code-Hygiene, dafür weiß ich vorher ziemlich genau, was ein Run produzieren wird.

Ersetzt das den Maintainer?

Nein. Was sich ersetzt hat, ist mein Engpass.

Vor Pixel war der Engpass, dass ich nach der Tagesarbeit zu müde war, das nächste Feature anzufangen. Jetzt ist der Engpass das Schreiben präziser Aufgaben-Prompts und das Reviewen der Branches. Reviewen liegt mir — kuratieren, Anmerkungen geben, Konventionen verfeinern. Das Prompt-Schreiben musste ich erst lernen: einen Task so formulieren, dass ihn jemand ohne Rückfragen erledigen kann, ist eine eigene Fähigkeit, und sie hilft mir seitdem auch im Job, wenn ich Aufgaben an Menschen übergebe.

puzzlekreis.de hätte ohne Pixel vielleicht ein Drittel der heutigen Featuredichte. Es bleibt eine kleine Webapp mit zwei Maintainern — einer davon ist rund um die Uhr verfügbar, der andere entscheidet, was überhaupt gebaut wird.


puzzlekreis.de ist inzwischen live. Wenn dich das Setup interessiert (oder du selber Puzzles tauschen willst), schreib mir.