CLAUDE.md als Briefing

CLAUDE.md als Briefing

Artikel 1 · Serie: Agentic Coding mit Claude Code

Wer Claude Code in ein Projekt schickt, schickt einen Agenten ohne Gedächtnis. Was das Modell mitbringt, ist allgemeines Training — was im konkreten Vorhaben gilt, muss ihm jede Session neu mitgegeben werden. Genau dafür ist CLAUDE.md da: eine Datei, die automatisch geladen wird, bevor der erste Befehl läuft. Stack, Sprache, Verzeichnis-Tabus, Stilregeln, was erlaubt ist und was nicht. Ein Lagebericht, kein Vertrag — präzise genug, damit der Agent weiß, worauf er sich einlässt, und knapp genug, um ihn nicht zu lähmen.

Das Bild dahinter ist das einer Einweisung vor dem Einsatz: Was ist die Situation, womit wird gearbeitet, was ist das Ziel, was ist tabu. Ohne dieses Briefing improvisiert der Agent aus dem allgemeinen Modell-Wissen heraus. Mit ihm zieht er los und weiß, was zu tun ist.

In diesem ersten Artikel der Reihe richten wir die vollständige Arbeitsumgebung für byhaushalt ein — ein Projekt, das den bayerischen Haushalt 2026/27 aus amtlichen PDFs auch maschinell und bearrierefrei lesbar machen soll. Wir installieren Claude Code, legen das Repository auf Codeberg an, schreiben CLAUDE.md, setzen Permissions und checken siebzehn Haushaltsdokumente ein. Am Ende steht Tag v0.1 — ein sauberer Ausgangspunkt, den wir in den folgenden Artikeln aufgreifen. Zwei Ansätze, die wir zwischendurch wieder verworfen haben, nehmen wir ebenfalls mit: nicht als Warnung, sondern weil sie zum tatsächlichen Ablauf gehören.

Claude Code installieren — CLI, App und Remote

Fangen wir ganz vorne an und das ist bei Claude Code selbst. Die Artikelreihe hier läuft auf einem Mac M3 bzw. M4, funktioniert aber genauso unter Linux. Mit der OS Parodie aus Redmond - aka Windows - sollte das genauso möglich sein, wird aber nicht gesondert behandelt. ( Sorry aber wer mit Windows entwickelt dem ist eh nicht mehr zu helfen ).

CLI vs. App

Claude Code ist in zwei lokalen Distributionen verfügbar. Die CLI kommt über npm:

npm install -g @anthropic-ai/claude-code
claude

oder wenn man auf dem Mac brew installiert hat

brew install claude
claude

Beim ersten Aufruf folgt ein OAuth-Flow mit dem Anthropic-Konto, dann landet man in einer interaktiven Shell. Kein Daemon, kein separater Prozess im Hintergrund — Claude Code startet, arbeitet, beendet sich.

Die Desktop-App (claude.ai/download) ist die zweite Option. Sie bringt denselben Agenten-Loop in ein natives Fenster, ergänzt durch eine visuelle Darstellung des Tool-Use: man sieht, welche Dateien gelesen werden, welche Bash-Befehle ausgeführt werden, wo Permissions greifen. Für den Einstieg ist das hilfreich — das Loop-Verhalten von Claude Code wird greifbarer, wenn man es live verfolgen kann. Der Nachteil: die App ist eine zusätzliche Applikation im Dock, kein Terminal-Werkzeug. Wer bereits in iTerm2 oder einem anderen Terminal lebt, wechselt dafür den Kontext.

Für diese Reihe ist die CLI die primäre Distribution. Die App bleibt optional — sinnvoll zum Beobachten, weniger für den Arbeitsfluss.

Remote Control

Remote Control verbindet claude.ai/code oder die Claude-App (iOS/Android) mit einer lokal laufenden Claude-Code-Session. Wichtig: Der Agent läuft dabei auf dem eigenen Rechner — Code, Repository und ausgeführte Befehle bleiben lokal. Die Web-Oberfläche und die Mobile-App sind reine Remote-UI-Fenster in diese Session, kein Cloud-Agent, keine zweite Instanz.

Aktivieren lässt sich das direkt in einer laufenden Session:

/remote-control byhaushalt

Oder beim Start über die CLI:

claude --remote-control "byhaushalt"

Wer Remote Control dauerhaft für alle Sessions einschalten will, findet die Option unter /config → „Enable Remote Control for all sessions".

Was passiert, wenn der Rechner schläft oder das Netz wegbricht? Die Session wird unterbrochen — sie läuft ja lokal. Remote Control ist allerdings auf Wiederverbindung ausgelegt: Sobald der Rechner wieder online ist, baut die App automatisch die Verbindung neu auf. Kein manueller Neustart nötig.

Typische Use Cases: einen längeren Task vom Telefon aus anstoßen, zwischendurch den Status prüfen, oder eine zweite Person lässt bei einem Pair-Programming-Moment über claude.ai/code zuschauen. Für byhaushalt nutzen wir das punktuell — nicht als zentrales Workflow-Element.

Abgrenzung: Davon zu unterscheiden ist „Claude Code on the web" — eine separate Funktion mit echten Cloud-Sandbox-Sessions, die keinerlei lokalen Rechner benötigen und sich zwischen Browser und Terminal verschieben lassen. Für die meisten Szenarien dieser Reihe ist das nicht relevant, aber wer den Begriffen begegnet, sollte wissen, dass es sich um zwei verschiedene Features handelt.

Praxis-Pattern für die Reihe

CLI primär, App optional zum Beobachten, Remote punktuell für Tasks, die laufen sollen während ich weg bin. Das gilt für alle Artikel dieser Reihe.

Warum CLAUDE.md zuerst kommt

Claude Code liest CLAUDE.md beim Start jeder Session. Nicht optional, nicht konfigurierbar — das ist das Protokoll. Die Datei ist damit das einzige persistente Gedächtnis zwischen Sessions, das nicht in Git-History oder Code-Kommentaren vergraben ist.

Das klingt nach Dokumentation. Es ist keine Dokumentation. Dokumentation beschreibt einen Zustand für menschliche Leser, die urteilen können, was relevant ist und was nicht. CLAUDE.md gibt einem Agenten operative Anweisungen, der ohne sie buchstäblich nicht weiß, in welcher Sprache Kommentare geschrieben werden sollen, ob er git push --force ausführen darf, ob er PDFs verändern darf. Jede fehlende Konvention ist eine implizite Erlaubnis, irgendetwas zu tun.

Für byhaushalt war das besonders konkret: Ohne explizites Verbot hätte Claude Code die Quelldaten in daten/ umbenennen, konvertieren oder anderweitig mutieren können. Das sind staatliche Haushaltsdaten, an die wir uns halten wollen — ihre Integrität ist nicht verhandelbar.

Was in CLAUDE.md gehört: Stack-Entscheidungen, Verzeichnisstruktur, Sprachregeln, Stilregeln, Verbote. Was nicht hineingehört, dazu kommen wir später.

Repo anlegen

Zuerst lokal, dann remote. git init im Projektverzeichnis, dann die Grundstruktur: ein leeres README, .gitignore, und den ersten Commit.

Die .gitignore deckt die üblichen Verdächtigen ab — macOS (.DS_Store), Python (__pycache__/, .venv/, *.pyc, .pytest_cache/, .ruff_cache/), Node (node_modules/, dist/), Editor-Müll (.vscode/, .idea/). Nichts Ungewöhnliches, aber besser früh als nach dem ersten Push mit drei Caching-Verzeichnissen im Repository.

Das Remote geht auf Codeberg. Codeberg ist ein selbst-gehostetes Gitea-Deployment, betrieben von einem deutschen Verein, datenschutzrechtlich auf festem Boden — für ein Projekt, das staatliche Haushaltsdaten verarbeitet, eine sinnvollere Wahl als GitHub. Die Repo-Erzeugung auf Codeberg ist simpel: Name, Sichtbarkeit, kein automatisches README (das kommt aus dem lokalen Repo). Remote setzen, ersten Commit pushen, fertig.

Dass der erste Anlauf unter dem falschen Namen landete, davon später mehr.

CLAUDE.md schreiben

Die fertige Datei für byhaushalt sieht so aus:

# byhaushalt — Projekt-Konventionen für Claude Code

byhaushalt extrahiert die amtlichen Haushaltspläne des Freistaats Bayern (PDF) und visualisiert sie als interaktive Webanwendung. Demo-Projekt für die Reihe „Agentic Coding mit Claude Code" auf rotecodefraktion.de.

## Stack

- Parsing: Python 3.12, uv, pdfplumber, polars
- Datenformat: Parquet + JSON-Manifest
- Frontend: Vite + React 19 + TypeScript + Tailwind + shadcn/ui
- Charts: Observable Plot
- Tests: pytest + Hypothesis (Python), Vitest + React Testing Library (Frontend), Playwright via MCP (E2E)

## Verzeichnisse

- `daten/` — Quell-PDFs, gepinnt, nicht ändern
- `parser/` — Python-Code, uv-managed
- `web/` — Frontend
- `docs/` — Architektur und ADRs
- `.claude/` — Skills, Commands, Hooks, Settings

## Sprache

- Code-Identifier: Englisch
- Kommentare: nur wenn nicht-offensichtlich (warum, nicht was)
- Doku in `docs/`: Deutsch
- Commit-Messages: Deutsch oder Englisch, knapp, Conventional-Commits-Stil
- Tests: deutsche Testbeschreibungen erlaubt

## Stilregeln

- Keine ASCII-Diagramme — Excalidraw oder Plot
- Keine Filler-Kommentare
- Keine TODO-Kommentare ohne Issue-Referenz
- Type Hints in Python überall
- TypeScript strict-mode

## Verbote

- Kein direkter Zugriff auf Web-APIs ohne Erlaubnis
- Keine destruktiven Git-Aktionen ohne explizite Anweisung
- Kein `--no-verify` bei Commits
- PDFs in `daten/` nicht ändern, nur lesen

## Test-Disziplin

- Test-First: Test zuerst schreiben, fail prüfen, dann implementieren
- Bei Implementierung: Output zeigen vor Commit

Die Kategorien sind nicht willkürlich. Stack gibt Claude Code den Kontext, welche Tools verfügbar sind und welche nicht — kein Requests statt httpx, kein Pandas statt Polars. Verzeichnisse legt fest, was wo liegt und — im Fall von daten/ — was nicht angefasst werden darf. Sprache verhindert, dass Kommentare auf Englisch landen, Commit-Messages auf Deutsch, und Testbeschreibungen irgendwo dazwischen. Stilregeln sind keine Ästhetik-Fragen — Filler-Kommentare wie # set variable x sind Rauschen, das bei jedem Review mitgelesen wird.

Die Verbote-Sektion verdient besondere Aufmerksamkeit. Verbote in CLAUDE.md sind nicht dekorativ. Sie sind eine erste Linie — die zweite Linie sind die Permissions in .claude/settings.json. git push --force und --no-verify sind dort nicht als Hinweise formuliert, sondern als explizite Negationen. Ein Agent, der diese Grenzen überschreitet, macht das nicht aus Bosheit, sondern weil nichts dagegen steht. Die Formulierung in CLAUDE.md stellt klar, dass diese Grenzen Grenzen sind.

Was nicht in CLAUDE.md gehört

Drei Kategorien, die regelmäßig in CLAUDE.md landen und dort nicht hingehören:

Architektur-Details — wie der Parser aufgebaut ist, welche Abstraktionsschichten es gibt, warum wir Parquet statt SQLite gewählt haben. Das gehört in docs/architecture.md oder in Architecture Decision Records. CLAUDE.md beschreibt Konventionen, nicht Zustände. Die Architektur kann sich ändern; die Konvention, wie Änderungen dokumentiert werden, bleibt stabil.

TODOs — offene Punkte gehören in Issues oder Pull Requests, nicht in CLAUDE.md. Ein TODO in der Konventionsdatei suggeriert, dass er dauerhaft gilt. Ein Issue hat einen Status, einen Assignee, eine Diskussion.

Build-Anweisungen für Menschenuv sync, pnpm install, pnpm dev. Diese gehören in README oder in Lockfiles. CLAUDE.md setzt voraus, dass der Mensch die Toolchain kennt; es ist kein Onboarding-Dokument.

Die Faustregel: Konventionen ja, Zustände nein. Was sich bei jedem Commit ändern könnte, gehört nicht in CLAUDE.md.

Permissions in .claude/settings.json

Die zweite Datei im .claude/-Verzeichnis ist .claude/settings.json. Sie definiert, was Claude Code auf Systemebene darf — unabhängig davon, was in CLAUDE.md steht. Die Settings sind eine härtere Grenze: CLAUDE.md kann umformuliert, missinterpretiert oder ignoriert werden; Permissions in settings.json werden vom Tool erzwungen.

Der relevante Auszug für byhaushalt:

{
  "permissions": {
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git commit:*)",
      "Bash(git tag:*)",
      "Bash(git push:*)",
      "Bash(uv:*)",
      "Bash(pytest:*)",
      "Bash(ruff:*)",
      "Bash(pnpm:*)",
      "Read(*)",
      "Edit(*)",
      "Write(*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)",
      "Bash(git reset --hard:*)",
      "Bash(curl:*)",
      "Bash(wget:*)"
    ]
  }
}

Die Allow-Liste ist explizit. Bash(git push:*) erlaubt Push, Bash(git push --force:*) in der Deny-Liste sperrt die destruktive Variante — beides gleichzeitig, die spezifischere Deny-Regel gewinnt. curl und wget sind geblockt: byhaushalt soll keine Daten aus dem Netz laden ohne explizite Freigabe. Lesen, Schreiben und Editieren von Dateien ist breit erlaubt — das ist das Kerngeschäft des Agenten.

Die Deny-Liste ist eine zweite Schicht, keine redundante. Selbst wenn die Allow-Liste zu weit formuliert wäre, fangen die Deny-Einträge das auf. Beide Listen zusammen ergeben ein Sicherheitsmodell, das für ein Analyseprojekt mit sensiblen Quelldaten angemessen ist.

Was du nicht tun solltest: –dangerously-skip-permissions

Claude Code kennt ein CLI-Flag namens --dangerously-skip-permissions. Der Name ist kein Zufall — Anthropic hat ihn bewusst so gewählt. Wer das Flag setzt, schaltet sämtliche Permission-Prompts ab. Allow-Liste, Deny-Liste, alles, was wir gerade in settings.json konfiguriert haben — ignoriert. Der Agent kann dann ohne Rückfrage Dateien löschen, Commits pushen, beliebige Netzwerk-Aufrufe machen.

Es gibt Kontexte, in denen das legitim ist: in einem Docker-Container oder einer isolierten VM, wo der Workspace bewusst weggeworfen werden kann, oder in einer CI-Pipeline ohne Zugriff auf produktive Systeme. Wer genau weiß, was sie tut, und die Bremse kurz bewusst lösen will, kennt das Risiko.

Für alle, die gerade erst mit Claude Code anfangen: lasst das Flag weg. Nicht weil ein Tool unnötig paranoid wäre, sondern weil der gesamte Schutzmechanismus, den wir in dieser Reihe aufbauen — Berechtigungsstruktur, Deny-Liste, CLAUDE.md-Anweisungen — mit einem einzigen Flag deaktiviert wird. byhaushalt verwendet --dangerously-skip-permissions nicht. Wer die Projekte dieser Reihe nachbaut: ebenfalls nicht.

Quelldaten ins Repo

byhaushalt arbeitet mit 17 amtlichen PDFs: der Gesamthaushalt 2026/27 und die Einzelpläne 01 bis 16. Sie kommen vom Bayerischen Staatsministerium der Finanzen, sind gemeinfrei, und liegen in daten/.

Wir haben sie direkt eingecheckt — kein Git LFS, keine externe Storage-Lösung. Die Gesamtgröße liegt bei etwa 22 MB. Das ist mit modernen Git-Hosting-Limits kein Problem, und der Vorteil ist erheblich: Ein git checkout v0.1 gibt einem neuen Mitarbeiter — oder Claude Code in einer neuen Session — sofort den vollständigen Datensatz, ohne externe Abhängigkeiten auflösen zu müssen. Reproduzierbarkeit ist hier keine akademische Tugend, sondern praktische Notwendigkeit: wenn der Parser mit einem bestimmten PDF-Stand kalibriert ist, soll der exakt gleiche Stand in sechs Monaten noch abrufbar sein.

Das Verbot aus CLAUDE.md greift hier direkt: PDFs in daten/ nicht ändern, nur lesen. Die Deny-Regel für rm -rf:* macht das auf Systemebene robuster. Die Quelldaten sind der unveränderliche Boden, auf dem alles andere aufbaut.

Erster Tag — v0.1

Nach vier Commits hatten wir einen Zustand, der einen Tag verdiente: Konventionen, Permissions, Daten — kein Anwendungscode.

git tag -a v0.1 -m "Init: CLAUDE.md, Permissions, Quelldaten (17 PDFs)"
git push origin v0.1

Der praktische Wert: v0.1 ist ein benannter Referenzpunkt. Wenn sich in einem späteren Artikel eine Konvention ändert, kann man sauber sagen: „ab v0.3 heißt das anders". Für Leser, die die Reihe nachvollziehen wollen, ist der Tag der Einstiegspunkt — clone, checkout, fertig.

Stand am Ende dieses Artikels

Das Repo enthält jetzt: vier Commits, einen Tag, 17 PDFs, drei Konfigurationsdateien — CLAUDE.md, .claude/settings.json, .gitignore — und kein Anwendungscode.

Zum Mitziehen:

git clone https://codeberg.org/rotecodefraktion/byhaushalt.git
cd byhaushalt
git checkout v0.1

Der vollständige Stand liegt unter byhaushalt @ v0.1.

Permissions-Wildcard. Der erste Entwurf der settings.json hatte Bash(*) in der Allow-Liste — alles erlaubt, Deny-Liste als einzige Einschränkung. Das sah kompakter aus. Es ist aber eine schlechtere Entscheidung: eine Wildcard-Allow-Liste sagt dem Agenten implizit, dass alles, was nicht explizit verboten ist, erwünscht ist. Eine explizite Allow-Liste sagt, was erwünscht ist. Der Unterschied klingt philosophisch, hat aber praktische Konsequenzen bei unerwarteten Situationen — wenn Claude Code einen Befehl ausführen will, der weder in der Allow- noch in der Deny-Liste steht, ist das Verhalten bei Bash(*) anders als bei einer expliziten Liste. Wir haben auf die explizite Variante umgestellt. Sie ist länger, aber klarer.

Wie es weitergeht

Im nächsten Artikel geht es um Plan Mode und Subagents. Bevor eine Zeile Parser-Code entsteht, muss die PDF-Struktur kartiert sein: wie sind die Haushaltspläne aufgebaut, welche Seitentypen gibt es, wo beginnen die Tabellen, wo enden sie. Das ist eine Analyseaufgabe — und genau der Fall, für den Plan Mode gebaut ist.

Für den Einstieg in die Grundlagen der Reihe — Vibe Coding vs. Agentic Coding, Tool-Use, Loop, Verification — ist der Prolog der richtige Ausgangspunkt.