# 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. ## Autoritative Dokumente @docs/specs/technik-und-architektur.md @docs/specs/fachliche-anforderungen.md Für die Umsetzung ist zusätzlich immer das aktuell aktive Arbeitspaket unter `docs/workpackages/` maßgeblich. Nicht raten, wenn Dokumente fehlen, unklar sind oder sich widersprechen. ## 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/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. **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 - Start über Windows Task Scheduler - kein Webserver - kein Applikationsserver - keine Dauerlauf-Anwendung - kein interner Scheduler - Log4j2 für Logging - SQLite als lokaler Persistenzspeicher - 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. - Konkrete Provider-Familie, Base-URL und Modellname sind **Konfiguration**, keine Architekturentscheidung. ## Verbindliche Modulstruktur - `pdf-umbenenner-domain` - `pdf-umbenenner-application` - `pdf-umbenenner-adapter-in-cli` - `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 und keine HTTP-Kommunikation - Application orchestriert Use Cases und enthält keine technischen Implementierungsdetails - Externe Zugriffe erfolgen ausschließlich über **Ports** - Konkrete technische Implementierungen sind **Adapter** - Adapter dürfen nicht direkt voneinander abhängen - 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 ## Globale fachliche Leitplanken - Zielformat: `YYYY-MM-DD - Titel.pdf` - Bei Namenskollisionen: `YYYY-MM-DD - Titel(1).pdf`, `YYYY-MM-DD - Titel(2).pdf`, ... - Die **20 Zeichen** gelten 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 - 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 Die fachliche und technische Basis ist vollständig umgesetzt, dokumentiert, getestet (inkl. PIT-Mutationstests, Smoke-Tests, End-to-End-Tests) und freigegeben. Der aktive Stand ist die Erweiterung **„Zusätzlicher KI-Provider Anthropic Claude über die native Messages API"**. Sie ist eine bewusst minimale Erweiterung des freigegebenen Basisstands. ### Ziel der aktiven Erweiterung - Der bestehende OpenAI-kompatible KI-Weg bleibt unverändert nutzbar. - Zusätzlich wird die **native Anthropic Messages API** als zweite, gleichwertig unterstützte Provider-Familie integriert. - Genau **ein** Provider ist pro Lauf aktiv – ausschließlich über Konfiguration ausgewählt. - **Kein** automatischer Fallback, **keine** Parallelnutzung, **keine** Profilverwaltung. - Der fachliche KI-Vertrag (`NamingProposal` aus Application-/Domain-Sicht) bleibt unverändert. - Bestehende Properties-Dateien aus dem Vorgängerstand werden beim ersten Start kontrolliert in das neue Schema migriert; vorher wird automatisch eine `.bak`-Sicherung angelegt. - Architekturgrenzen, Persistenzmodell, Statussemantik, Retry-Semantik, Exit-Code-Verhalten und Logging-Mindestumfang bleiben unverändert; sie werden ausschließlich um den Provider-Identifikator und die Provider-Auswahl ergänzt. ## 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. - Proposal-Versuch mit fachlich unbrauchbarem Titel oder Datum = inkonsistenter Persistenzzustand = dokumentbezogener technischer Fehler. - Inkonsistente Proposal-Zustände werden **nicht stillschweigend geheilt**, sondern als technische Dokumentfehler behandelt. ## Retry-Semantik ### Deterministische Inhaltsfehler Deterministische Inhaltsfehler sind insbesondere: - kein brauchbarer Text - Seitenlimit überschritten - fachlich unbrauchbarer oder generischer Titel - vorhandenes, aber unbrauchbares KI-Datum Regel: - **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. - Sie bleiben 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. - Die Klassifikation gilt provider-unabhängig: Technische Fehler aus dem aktiven KI-Provider werden in dieselbe transiente Kategorie eingeordnet wie bisher. Der inaktive Provider wird in keiner Fehlersituation als Backup verwendet. ### 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. - Liefert genau ein dokumentbezogenes Ergebnis für Persistenz und Statusfortschreibung. ### Skip-Semantik - `SUCCESS` → in späteren Läufen `SKIPPED_ALREADY_PROCESSED` historisieren, keine Zähleränderung. - `FAILED_FINAL` → in späteren Läufen `SKIPPED_FINAL_FAILURE` historisieren, keine Zähleränderung. - `FAILED_RETRYABLE`, `READY_FOR_AI`, `PROPOSAL_READY` → verarbeitbar. ## Logging-Mindestumfang Folgende Informationen müssen nachvollziehbar geloggt werden: - Laufstart mit Lauf-ID - aktiver KI-Provider für den Lauf - Laufende - erkannte Quelldatei - Überspringen bereits erfolgreicher Dateien - Überspringen final fehlgeschlagener Dateien - erzeugter Zielname - Retry-Entscheidung - Fehler mit Klassifikation ### Korrelationsregel - Vor erfolgreicher Fingerprint-Ermittlung: Korrelation über Lauf-ID und Kandidatenbezug. - Nach erfolgreicher Fingerprint-Ermittlung: dokumentbezogene Logs enthalten den Fingerprint oder eine eindeutig ableitbare Referenz. - Keine neue Persistenz-Wahrheit oder zusätzliche Tracking-Ebene. ### Sensibilitätsregel für KI-Inhalte - Vollständige KI-Rohantwort: standardmäßig **nicht** ins Log, bleibt in SQLite. - Vollständiges KI-`reasoning`: standardmäßig **nicht** ins Log, bleibt in SQLite. - Freischaltung nur über expliziten booleschen Konfigurationswert. - Default: sicher / nicht loggen. - Die Sensibilitätsregel gilt provider-unabhängig. ## Verarbeitungsreihenfolge pro Dokument 1. Fingerprint berechnen 2. Dokument-Stammsatz laden 3. Terminale Skip-Fälle entscheiden (`SUCCESS` → `SKIPPED_ALREADY_PROCESSED`, `FAILED_FINAL` → `SKIPPED_FINAL_FAILURE`) 4. Falls nötig: Pfad bis `PROPOSAL_READY` durchlaufen (inkl. KI-Aufruf über den aktiven Provider) 5. Führenden `PROPOSAL_READY`-Versuch laden 6. Finalen Basis-Dateinamen bilden 7. Dubletten-Suffix im Zielordner bestimmen 8. Zielkopie schreiben (temporäre Datei + finaler Move/Rename; bei Fehler: genau ein Sofort-Wiederholversuch) 9. Retry-Entscheidung ableiten 10. Neuen Versuch historisieren, Stammsatz konsistent fortschreiben ## Zielkopie-Semantik - Kopie zunächst in temporäre Zieldatei im Zielkontext - Finaler Move/Rename auf den geplanten Zieldateinamen - Quelldatei bleibt **immer unverändert** - Bei technischem Schreibfehler: genau ein Sofort-Wiederholversuch (nur Zielkopierpfad) - Bei Persistenzfehler nach erfolgreicher Zielkopie: kein `SUCCESS` setzen, best-effort Rückbau der Zielkopie, Ergebnis bleibt dokumentbezogener technischer Fehler ## Fehlersemantik - Technische Fehler → `FAILED_RETRYABLE`, Transientfehlerzähler +1 - Bei Erreichen von `max.retries.transient` → `FAILED_FINAL` - Kein Abbruch des Batch-Laufs für andere Dokumente - Keine neue finale Fehlerkategorie - Vor-Fingerprint-Fehler werden **nicht** als SQLite-Versuch historisiert - Provider-spezifische Fehlerausprägungen (HTTP-Fehler, Auth-Fehler, Antwort-Schema-Fehler) werden im jeweiligen Adapter klassifiziert und auf die bestehenden Fehlerkategorien abgebildet. Es entstehen keine neuen Fehlerklassen. ## Persistenz Zwei-Ebenen-Modell bleibt unverändert – 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. Schema-Erweiterungen sind additiv mit definierten Defaultwerten für historische Versuche ohne Provider-Identifikator. ## Naming-Regel (verbindlich für alle Arbeitspakete) In Implementierungen, Kommentaren und JavaDoc dürfen **keine** Meilenstein- oder Arbeitspaket-Bezeichner erscheinen: - Verboten: `M1`, `M2`, …, `M8` - Verboten: `AP-001`, `AP-002`, … `AP-00x` - Verboten: Versionsbezeichner wie `V1.0`, `V1.1` 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 OpenAI-kompatiblen KI-Weg ## 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 und Tests ergänzt sind, **soweit für den Stand sinnvoll** - 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: - - ... - Build-Kommando: - Build-Status: ERFOLGREICH / FEHLGESCHLAGEN - Offene Punkte: keine / - Risiken: keine / ``` ## 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: `.\mvnw.cmd clean verify -pl pdf-umbenenner-domain,pdf-umbenenner-application,pdf-umbenenner-adapter-out,pdf-umbenenner-adapter-in-cli,pdf-umbenenner-bootstrap --also-make` - 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 - Exit-Code `0`: Lauf technisch ordnungsgemäß ausgeführt, auch wenn einzelne Dateien fachlich oder transient fehlgeschlagen sind - Exit-Code `1`: harter Start-/Bootstrap-Fehler - 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; zulässige Werte sind die Bezeichner der unterstützten Provider-Familien) - `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 - `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 mit zweckmäßig: - Modellname - API-Schlüssel (Umgebungsvariable hat Vorrang) - Timeout - Basis-URL (optional, wo betrieblich sinnvoll) Konkretes Schema (zweckmäßig): ```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 (mit flachen Schlüsseln 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 landen im Namensraum **`openai-compatible`** - `ai.provider.active` wird auf `openai-compatible` gesetzt 4. Datei in-place schreiben 5. Datei erneut laden und validieren 6. Erst danach den normalen Lauf fortsetzen Alte und neue Struktur sind **kein** dauerhaft gleichrangiges Endformat. ## Nicht-Ziele / Verbote - 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 (OpenAI-kompatibel, Anthropic Messages API) - keine stillen Änderungen am bestehenden OpenAI-kompatiblen KI-Weg - kein Sofort-Wiederholversuch außerhalb des Zielkopierpfads - keine Reporting- oder Statistikfunktionen - keine neue dritte Persistenz-Wahrheitsquelle für Retry-Entscheidungen - keine neue Fachfunktionalität jenseits des definierten Zielbilds - kein großflächiges Refactoring ohne nachweisbaren Defektbezug - keine spekulativen Umbauten ohne konkreten Qualitäts- oder Konsistenzbezug - keine Vermischung von API-Schlüsseln verschiedener Provider-Familien