# CLAUDE.md

## Zweck
Dieses Repository implementiert einen lokal gestarteten **PDF-Umbenenner mit KI**. Das Programm liest bereits OCR-verarbeitete, durchsuchbare PDF-Dateien aus einem Quellordner, ermittelt daraus einen normierten Dateinamen und legt **eine Kopie** der Datei im Zielordner ab. Die Quelldatei bleibt unverändert.

Ab V2.0 wird die Anwendung um eine **lokale JavaFX-Desktop-GUI** erweitert. Die GUI ist der neue Standardstart. Der bestehende headless Batch-Betrieb bleibt über `--headless` vollständig erhalten.

## Autoritative Dokumente
@docs/specs/technik-und-architektur.md
@docs/specs/fachliche-anforderungen.md
@docs/specs/meilensteine-v2_0.md

Für die Umsetzung ist zusätzlich immer das aktuell aktive Arbeitspaket unter `docs/workpackages/` maßgeblich.
Dateinamensschema: `M9 - Arbeitspakete.md`, `M10 - Arbeitspakete.md`, … `M13 - Arbeitspakete.md`, `M14_-_Arbeitspakete.md`, `M15_-_Arbeitspakete.md`.
Nicht raten, wenn Dokumente fehlen, unklar sind oder sich widersprechen.

## Modulare Architektur-Übersichten
Detailwissen über Pakete, Schlüsselklassen, Ports und Bootstrap-Verdrahtung ist in drei modularen Übersichtsdokumenten unter `docs/architecture/` ausgelagert. Wer in einem bestimmten Modul arbeitet, liest diese Datei zusätzlich zu CLAUDE.md:

- `docs/architecture/domain-overview.md` – `pdf-umbenenner-domain` und `pdf-umbenenner-application`: Domänenmodell, Inbound- und Outbound-Ports, Application-Services.
- `docs/architecture/gui-overview.md` – `pdf-umbenenner-adapter-in-gui`: Workspace-/Tab-Struktur, View-Modelle, GUI-interne Ports, JavaFX-Threading-Modell.
- `docs/architecture/adapter-overview.md` – `pdf-umbenenner-adapter-out`, `pdf-umbenenner-adapter-in-cli`, `pdf-umbenenner-bootstrap`: konkrete Outbound-Adapter, CLI-Einstiegspunkt, Verdrahtungslogik und Provider-Auswahl.

Für Arbeit ausschließlich in einem dieser Bereiche genügt CLAUDE.md plus die jeweils passende Übersichtsdatei.

## Priorisierung der Regeln
Die Dokumente haben folgende feste Bedeutung:

- `docs/specs/technik-und-architektur.md` = verbindliche technische Zielarchitektur
- `docs/specs/fachliche-anforderungen.md` = verbindliche fachliche Regeln
- `docs/specs/meilensteine-v2_0.md` = verbindliche V2.0-Zieldefinition und Meilensteinabgrenzung
- `docs/workpackages/...` = verbindlicher Scope, Reihenfolge und Inhalt des aktuell bearbeiteten Arbeitspakets

Bei Konflikten gilt folgende Priorität:

1. **Technik- und Architektur-Dokument**
   Verbindliche technische Zielarchitektur. Architekturbrüche sind unzulässig.

2. **Fachliche Anforderungen**
   Verbindliche fachliche Regeln und fachliches Zielverhalten.

3. **Meilensteine V2.0**
   Verbindliche Zieldefinition, Abgrenzungen und Leitplanken für den V2.0-Ausbau.

4. **Arbeitspakete**
   Definieren den konkret erlaubten Umsetzungsumfang des aktuellen Schritts.

Wenn Dokumente fehlen, unklar sind oder sich widersprechen, nicht raten und keine stillen Annahmen treffen.

## Unverrückbare Technikvorgaben
- Java 21
- Maven Multi-Module
- ausführbares Standalone-JAR (ein gemeinsames JAR für GUI und headless)
- **GUI ist der neue Standardstart** (ab V2.0)
- `--headless` startet weiterhin den bestehenden Batch-/Scheduler-Betrieb
- `--config <pfad>` steht für GUI und headless zur Verfügung
- kein Webserver
- kein Applikationsserver
- keine Dauerlauf-Anwendung
- kein interner Scheduler
- das Shade-JAR bleibt das primäre Distributionsartefakt
- zusätzlicher nativer Windows-Installer (MSI) ab V3.0 via Maven-Profil `release` (jpackage, WiX Toolset 3.x im PATH erforderlich); der Normalbuild `mvn clean verify` bleibt vom Profil unberührt und benötigt kein WiX
- Log4j2 für Logging
- SQLite als lokaler Persistenzspeicher
- JavaFX wird mit dem JAR ausgeliefert (kein separates JavaFX-Setup)
- GUI offiziell nur für Windows; headless bleibt für Windows Server / Task Scheduler geeignet
- gemappte Laufwerke wie `S:\` oder `H:\` sind ausdrücklich zu unterstützen
- KI-Anbindung über genau **eine** der beiden unterstützten Provider-Familien:
  - **OpenAI-kompatible HTTP-Schnittstelle** (Chat-Completions-Stil)
  - **native Anthropic Messages API** (Claude-Modelle)
- Pro Lauf ist genau ein Provider aktiv. Kein Fallback, keine Parallelnutzung.
- `.properties` bleibt die einzige Konfigurationswahrheit
- Konkrete Provider-Familie, Base-URL und Modellname sind **Konfiguration**, keine Architekturentscheidung.

## Verbindliche Modulstruktur (ab V2.0)
- `pdf-umbenenner-domain`
- `pdf-umbenenner-application`
- `pdf-umbenenner-adapter-in-cli`
- `pdf-umbenenner-adapter-in-gui`
- `pdf-umbenenner-adapter-out`
- `pdf-umbenenner-bootstrap`

## Architekturregeln
- Strikte **hexagonale Architektur / Ports and Adapters**
- Abhängigkeiten zeigen immer **nach innen**
- Domain kennt **keine** Infrastruktur, keine Datenbank, kein Dateisystem, keine HTTP-Kommunikation und **kein JavaFX**
- Application enthält keine technischen Implementierungsdetails und **keine JavaFX-Typen**
- Externe Zugriffe erfolgen ausschließlich über **Ports**
- Konkrete technische Implementierungen sind **Adapter**
- Adapter dürfen nicht direkt voneinander abhängen
- Die GUI ist ein **Inbound-Adapter** (`pdf-umbenenner-adapter-in-gui`) – sie wird **nicht** in Bootstrap vermischt
- Bootstrap bleibt verantwortlich für: Startmoduswahl, Konfigurationsauflösung, Objektgraph, kontrollierten Start des passenden Adapters, Exit-Code-Ableitung bei harten Startfehlern
- GUI-Code und JavaFX dürfen im **headless Pfad** nicht unnötig früh initialisiert oder geladen werden
- Keine Vermischung von Dateisystem, PDF-Auslese, SQLite, KI-HTTP, Konfiguration, Logging, Benennungslogik und Retry-Entscheidungen
- Logging ist technische Infrastruktur, kein fachlicher Port
- Port-Verträge enthalten weder `Path`/`File` noch NIO- oder JDBC-Typen
- Der `AiNamingPort` bleibt provider-neutral; provider-spezifische Typen, Header, URLs und Antwortstrukturen leben ausschließlich in der jeweiligen Adapter-Out-Implementierung
- Es gibt keine gemeinsame „abstrakte KI-Adapter"-Zwischenschicht zwischen Port und konkreten Adaptern
- Die Bootstrap-Schicht wählt die **eine** aktive `AiNamingPort`-Implementierung anhand der Konfiguration aus

## GUI-Leitplanken (verbindlich für alle V2.0-Arbeitspakete)

### Threadingmodell
- Jede potenziell blockierende Operation (Modellabruf, technische Tests, Pfad-/Dateisystemprüfungen, SQLite-Prüfungen, Lesen/Schreiben der `.properties`) läuft auf einem **Hintergrund-Worker-Thread**
- UI-Updates erfolgen ausschließlich über den **JavaFX Application Thread** (`Platform.runLater`)
- Die GUI darf während laufender Hintergrund-Operationen **nicht einfrieren**

### GUI-Teststrategie
- **View-Modelle und Application-nahe GUI-Logik** werden mit JUnit unit-getestet; Fokus auf Zustandsübergängen, Validierungsregeln und Datenflüssen – nicht auf Rendering
- **GUI-Smoke-Tests** laufen unter **headless JavaFX (Monocle)** in der Maven-Test-Phase
- Kein TestFX, kein weiteres GUI-Testframework über Monocle hinaus

### GUI-Logging
- Der GUI-Adapter nutzt denselben Log4j2-Stack wie der headless Pfad
- Logformat und Log-Verzeichnis bleiben gegenüber dem headless Betrieb unverändert
- Mindesteinträge für GUI-nahe Ereignisse: Start- und Beendigungsereignisse der GUI, Modellabruf-Versuche (Provider, Erfolg/Misserfolg, **ohne API-Key**), Dateischreibvorgänge inkl. Zielpfad, Ergebnisse der Aktionen `Validieren` und `Technische Tests ausführen`, sowie alle schreibenden Korrekturen

### `--config`-Semantik
- Zeigt `--config <pfad>` im **GUI-Start** auf eine nicht existente Datei: Fehlermeldung, danach verhält sich die GUI so, als wäre `--config` nicht angegeben worden
- Zeigt `--config <pfad>` im **headless Start** auf eine nicht existente Datei: **harter Startfehler**, kein stiller Fallback

## JavaDoc-Standard (verbindlich für alle V2.0-Arbeitspakete)
Für jede **neu hinzugefügte** oder **substanziell geänderte** öffentliche Klasse, öffentliche Methode und jedes neue Java-Package gilt:
- **Klassen-JavaDoc**: Zweck, Verantwortung und Abgrenzung der Klasse
- **Methoden-JavaDoc**: Zweck, Parameter, Rückgabewert und dokumentierte Ausnahmen
- **`package-info.java`**: pro neuem Package, mit Kurzbeschreibung der Paketverantwortung

Ein Arbeitspaket ist erst fertig, wenn die betroffenen öffentlichen Klassen und Methoden dem JavaDoc-Standard entsprechen.

## Globale fachliche Leitplanken
- Zielformat: `YYYY-MM-DD - Titel.pdf`
- Bei Namenskollisionen: `YYYY-MM-DD - Titel(1).pdf`, `YYYY-MM-DD - Titel(2).pdf`, ...
- Die **konfigurierte maximale Titellänge** gilt nur für den **Basistitel**; das Dubletten-Suffix zählt nicht mit
- Das Dubletten-Suffix wird unmittelbar vor `.pdf` angehängt
- Titel sind **deutsch**, verständlich, eindeutig und enthalten keine Sonderzeichen außer Leerzeichen, Bindestrichen, Punkten, Kommas und Ampersands
- Eigennamen bleiben unverändert
- Datumsermittlung mit Priorität aus den fachlichen Anforderungen; wenn kein belastbares Datum eindeutig ableitbar ist, ist das **aktuelle Datum** als Fallback erlaubt
- Mehrdeutige Dokumente liefern **kein** unsicheres Ergebnis, sondern einen Fehler
- Erfolgreich verarbeitete Dateien werden nicht erneut verarbeitet
- Retryable fehlgeschlagene Dateien dürfen in späteren Läufen erneut verarbeitet werden
- Final fehlgeschlagene Dateien werden in späteren Läufen übersprungen
- Identifikation erfolgt **nicht** über Dateinamen
- Quelldateien werden **nie** überschrieben, verändert, verschoben oder gelöscht

## Aktiver Implementierungsstand
V1.1 ist vollständig umgesetzt, dokumentiert, getestet und freigegeben.

Der Basisstand V2.0 (JavaFX-GUI als Standardstart, Konfigurationseditor, technische Tests) ist abgeschlossen.

**V2.9 ist abgeschlossen.** Der Tab „Verarbeitungslauf" wurde erweitert um eine integrierte PDF-Vorschau (Lazy-Rendering direkt über PDFBox, In-Memory-Cache, Seitennavigation) sowie einen editierbaren Dateiname-Bereich mit Live-Validierung, Dirty-State-Dialog und atomarer Dateisystem-/DB-Transaktion inklusive Rollback und Fingerprint-basierter Konfliktauflösung. Die zugehörigen neuen Ports, Use Cases und Adapter sind in den modularen Architektur-Übersichten beschrieben.

Verhaltensänderungen seit V2.9: Die GUI startet maximiert, und die zuletzt geladene Konfigurationsdatei wird beim Start automatisch wieder geladen; existiert sie nicht mehr, startet die GUI ohne Fehlermeldung mit dem Willkommenstext.

Die fachliche Kernverarbeitung des PDF-Umbenenners bleibt unverändert.

## Statussemantik

| Status | Bedeutung |
|---|---|
| `READY_FOR_AI` | Verarbeitbar, KI-Pfad noch nicht durchlaufen |
| `FAILED_RETRYABLE` | Verarbeitbar, transient fehlgeschlagen |
| `PROPOSAL_READY` | Eingangszustand für Dateinamensbildung und Zielkopie |
| `SUCCESS` | Terminaler Enderfolg – nur nach Zielkopie und konsistenter Persistenz zulässig |
| `FAILED_FINAL` | Terminal, wird nicht erneut fachlich verarbeitet |
| `SKIPPED_ALREADY_PROCESSED` | Historisierter Skip für SUCCESS-Dokumente |
| `SKIPPED_FINAL_FAILURE` | Historisierter Skip für FAILED_FINAL-Dokumente |

### SUCCESS-Bedingung (verbindlich)
`SUCCESS` darf erst gesetzt werden, wenn:
1. die Zielkopie erfolgreich geschrieben wurde,
2. der finale Zieldateiname bestimmt ist,
3. die Persistenz konsistent fortgeschrieben wurde.

### Führende Quelle des Benennungsvorschlags (verbindlich)
- Die führende Quelle für Datum, Datumsquelle, validierten Titel und Reasoning ist der **neueste Versuchshistorieneintrag mit Status `PROPOSAL_READY`**
- Kein Rekonstruieren aus dem Dokument-Stammsatz
- Kein neuer KI-Aufruf, wenn bereits ein nutzbarer `PROPOSAL_READY`-Versuch vorliegt
- Status `PROPOSAL_READY` ohne lesbaren konsistenten Proposal-Versuch = dokumentbezogener technischer Fehler
- Inkonsistente Proposal-Zustände werden **nicht stillschweigend geheilt**, sondern als technische Dokumentfehler behandelt

## Retry-Semantik

### Deterministische Inhaltsfehler
- **erster** historisierter deterministischer Inhaltsfehler → `FAILED_RETRYABLE`
- **zweiter** historisierter deterministischer Inhaltsfehler → `FAILED_FINAL`

### Transiente technische Fehler
- Transiente Fehler laufen über den Transientfehlerzähler im Dokument-Stammsatz
- Retryable bis der konfigurierte Grenzwert `max.retries.transient` erreicht ist
- Der Fehlversuch, der den Grenzwert **erreicht**, finalisiert den Dokumentstatus zu `FAILED_FINAL`
- `max.retries.transient` = **Integer >= 1**; der Wert `0` ist ungültige Startkonfiguration

### Technischer Sofort-Wiederholversuch
- **Genau ein** zusätzlicher technischer Schreibversuch innerhalb desselben Dokumentlaufs
- **Ausschließlich** für Fehler beim physischen Zielkopierpfad
- Kein erneuter KI-Aufruf, keine erneute Fachableitung
- Zählt **nicht** zum laufübergreifenden Transientfehlerzähler

## Exit-Codes (verbindlich)
- **`0`**: normale erfolgreiche Beendigung eines headless Laufs sowie für das reguläre Beenden der GUI
- **`1`**: harte Start-, Bootstrap-, Verdrahtungs-, Konfigurations- oder Initialisierungsfehler, einschließlich ungültiger CLI-Verwendung, nicht existenter `--config`-Datei im headless Start und GUI-Startfehlern vor erfolgreicher Anzeige der Oberfläche
- Dokumentbezogene Verarbeitungsfehler im headless Lauf ändern dieses Exit-Code-Modell nicht

## Persistenz

Zwei-Ebenen-Modell – keine dritte Wahrheitsquelle.

**Dokument-Stammsatz** enthält u.a.:
- letzten Zielpfad, letzten Zieldateinamen
- Inhaltsfehler- und Transientfehlerzähler
- Gesamtstatus

**Versuchshistorie** enthält u.a.:
- finalen Zieldateinamen
- Fehlerklasse, Fehlermeldung, Retryable-Flag
- **Provider-Identifikator des aktiven KI-Providers für den Versuch**

**Invariante:** Der führende `PROPOSAL_READY`-Versuch wird nicht überschrieben. Jeder Lauf erzeugt einen **zusätzlichen** neuen Versuchseintrag.

**Rückwärtsverträglichkeit:** Bestehende Datenbestände bleiben lesbar, fortschreibbar und korrekt interpretierbar.

## Naming-Regel (verbindlich für alle Arbeitspakete)
In Implementierungen, Kommentaren und JavaDoc dürfen **keine** Meilenstein- oder Arbeitspaket-Bezeichner erscheinen:

- Verboten: `M1`, `M2`, …, `M13`
- Verboten: `AP-001`, `AP-002`, … `AP-0xx`
- Verboten: Versionsbezeichner wie `V1.0`, `V1.1`, `V2.0` in Code/JavaDoc

Stattdessen werden **zeitlose technische Bezeichnungen** verwendet.
Bestehende Kommentare mit solchen Bezeichnern, die durch eigene Änderungen berührt werden, sind zu ersetzen.

## Arbeitsweise
- Arbeite immer nur im **explizit aktiven Arbeitspaket**
- **Kein Vorgriff** auf spätere Arbeitspakete
- Änderungen klein, fokussiert und architekturtreu halten
- Keine unnötigen Umbenennungen, keine großflächigen Refactorings ohne Not
- Vor Änderungen zuerst die betroffenen Dateien und Abhängigkeiten verstehen
- **Keine Annahmen über Dateipfade.** Typen und Klassen werden per Suche nach Typname gefunden, nicht über vermutete Pfade.
- Keine Vermutungen: Bei echter Unklarheit oder Dokumentkonflikten knapp nachfragen oder den Konflikt benennen
- Keine stillen Änderungen am bestehenden headless Batch-Betrieb
- GUI-Code darf den headless Pfad nicht unnötig früh initialisieren

## Definition of Done pro Arbeitspaket
Ein Arbeitspaket ist erst fertig, wenn:
- der Zielumfang des aktuellen Arbeitspakets vollständig umgesetzt ist
- der Stand konsistent, fehlerfrei und buildbar ist
- Implementierung, Konfiguration, JavaDoc (inkl. `package-info.java` für neue Packages) und Tests ergänzt sind
- der JavaDoc-Standard für alle neu hinzugefügten oder substanziell geänderten öffentlichen Klassen und Methoden eingehalten wurde
- keine Inhalte späterer Arbeitspakete vorweggenommen wurden
- der Zwischenstand in sich geschlossen und übergabefähig ist

## Pflicht-Output-Format nach jedem Arbeitspaket

```
- Scope erfüllt: ja/nein
- Geänderte Dateien:
  - <Dateipfad>
  - ...
- Build-Kommando: <verwendetes Kommando>
- Build-Status: ERFOLGREICH / FEHLGESCHLAGEN
- Offene Punkte: keine / <Beschreibung>
- Risiken: keine / <Beschreibung>
```

## Qualitäts- und Prüfreihenfolge
- Nur den für das aktuelle Arbeitspaket nötigen Scope ändern
- Nach Änderungen den kleinsten sinnvollen Build-/Test-Umfang ausführen
- Build-Validierung vom Parent-Root (Beispiel für vollständigen Reactor-Build ab V2.0):
  `.\mvnw.cmd clean verify -pl pdf-umbenenner-domain,pdf-umbenenner-application,pdf-umbenenner-adapter-out,pdf-umbenenner-adapter-in-cli,pdf-umbenenner-adapter-in-gui,pdf-umbenenner-bootstrap --also-make`
- MSI-Build (nur lokal auf der Entwicklungsmaschine, WiX Toolset 3.x im PATH erforderlich):
  `.\mvnw.cmd clean package -P release -pl pdf-umbenenner-packaging --also-make -DskipTests`
- Schlägt der Build fehl: Fehler beheben, erneut bauen, erst dann weiter
- Vor Abschluss sicherstellen, dass der relevante Maven-Reactor-Stand fehlerfrei ist
- Fehler nicht kaschieren; Ursachen sauber beheben oder offen benennen

## Wichtige Betriebsregeln
- Ungültige Startkonfiguration verhindert den Verarbeitungslauf und führt zu Exit-Code `1`
- Eine ungültige oder fehlende Provider-Auswahl ist eine ungültige Startkonfiguration
- Run-Lock verhindert parallele Instanzen; wenn bereits eine Instanz läuft, beendet sich die neue Instanz sofort
- API-Schlüssel: pro Provider eine eigene Umgebungsvariable, Vorrang vor Properties derselben Provider-Familie. Schlüssel verschiedener Provider werden niemals vermischt.
- Dokumentbezogene Fehler führen **nicht** zu Exit-Code `1`

## Konfigurationsparameter
Verbindlich zweckmäßige Parameter:
- `source.folder` – Quellordner
- `target.folder` – Zielordner (muss vorhanden oder anlegbar sein, Schreibzugriff erforderlich)
- `sqlite.file` – SQLite-Datenbankdatei
- `ai.provider.active` – aktiver KI-Provider (Pflicht)
- `max.retries.transient` – max. historisierte transiente Fehlversuche pro Fingerprint (**Integer >= 1**, `0` ist ungültig)
- `max.pages` – Seitenlimit
- `max.text.characters` – maximale Zeichenzahl für KI-Eingabe
- `max.title.length` – maximale Länge des Basistitels in Zeichen (gültiger Bereich 10..120, Default 60)
- `prompt.template.file` – externe Prompt-Datei
- `log.ai.sensitive` – sensible KI-Logausgabe freischalten (Boolean, Default: `false`)
- `runtime.lock.file` – Lock-Datei (optional)
- `log.directory` – Log-Verzeichnis (optional)

Pro Provider-Familie existiert ein eigener Parameter-Namensraum:

```properties
ai.provider.active=openai-compatible

ai.provider.openai-compatible.baseUrl=...
ai.provider.openai-compatible.model=...
ai.provider.openai-compatible.timeoutSeconds=...
ai.provider.openai-compatible.apiKey=...

ai.provider.claude.baseUrl=...
ai.provider.claude.model=...
ai.provider.claude.timeoutSeconds=...
ai.provider.claude.apiKey=...
```

### Migration historischer Konfiguration
Bestehende Properties-Dateien des Vorgängerstands (flache Schlüssel wie `api.baseUrl`, `api.model`, `api.timeoutSeconds`, `api.key`) werden beim ersten Start erkannt und kontrolliert in das neue Schema überführt.

Verbindlicher Ablauf:
1. Legacy-Form erkennen
2. **`.bak`-Sicherung** der Originaldatei anlegen
3. Inhalt in das neue Schema überführen (Legacy-Werte → Namensraum `openai-compatible`, `ai.provider.active=openai-compatible`)
4. Datei in-place schreiben
5. Datei erneut laden und validieren
6. Erst danach den normalen Lauf fortsetzen

## Nicht-Ziele / Verbote
- kein manueller Verarbeitungslauf aus der GUI (kein vollständiger Lauf; Bearbeitungen nach Lauf sind zulässig)
- kein DB-/Historien-Tab in der GUI (erst V2.x+)
- kein Kosten-Tracking (erst V2.x+)
- kein echter Mini-KI-Testaufruf mit fachlicher Antwortauswertung
- kein Web-UI
- keine REST-API zur Bedienung
- keine OCR innerhalb der Java-Anwendung
- keine DMS-Funktionalität
- kein menschlicher Review-Workflow in der Anwendung
- keine interne Scheduler-Logik
- keine Architekturbrüche
- keine neuen Bibliotheken oder Frameworks ohne klare Notwendigkeit und Begründung
- **keine** automatische Fallback-Umschaltung zwischen KI-Providern
- **keine** parallele Nutzung mehrerer KI-Provider in einem Lauf
- **keine** Profilverwaltung mit mehreren Konfigurationen je Provider-Familie
- **keine** Provider-Familien jenseits der explizit unterstützten
- kein neues Konfigurationsformat
- keine Änderung der fachlichen Kernverarbeitung des PDF-Umbenenners
- keine Änderung der bestehenden Status-, Retry- oder Persistenz-Wahrheit
- keine stillen Änderungen am bestehenden headless Batch-Betrieb
- kein Sofort-Wiederholversuch außerhalb des Zielkopierpfads
- keine spekulativen Umbauten ohne konkreten Qualitäts- oder Konsistenzbezug
- kein großflächiges Refactoring ohne nachweisbaren Defektbezug
