# Betriebsdokumentation – PDF Umbenenner

## Zweck

Der PDF Umbenenner liest bereits OCR-verarbeitete, durchsuchbare PDF-Dateien aus einem
konfigurierten Quellordner, ermittelt per KI-Aufruf einen normierten deutschen Dateinamen
und legt eine Kopie im konfigurierten Zielordner ab. Die Quelldatei bleibt unverändert.

---

## Startmodi und Betriebsmodell (V2.0)

Ab V2.0 enthält die Anwendung zwei Startmodi in **einem gemeinsamen ausführbaren JAR**:

| Startmodus | Beschreibung |
|---|---|
| **GUI-Start** (Standard) | Öffnet die JavaFX-Desktop-GUI. Wird verwendet, wenn kein `--headless` angegeben ist. |
| **headless Betrieb** | Klassischer Batch-/Scheduler-Betrieb ohne grafische Oberfläche. Wird über `--headless` aktiviert. |

### CLI-Optionen

| Option | Beschreibung |
|---|---|
| *(keine Argumente)* | GUI-Standardstart |
| `--headless` | Aktiviert den headless Batch-Betrieb (wie vor V2.0) |
| `--config <pfad>` | Zeigt explizit auf eine `.properties`-Konfigurationsdatei (für GUI und headless) |

`--config` und `--headless` können kombiniert werden:

```
java -jar pdf-umbenenner-bootstrap-*.jar --headless --config C:\Pfad\zur\config.properties
```

### Verhalten bei fehlender oder ungültiger `--config`-Datei

| Startmodus | Datei nicht vorhanden | Datei vorhanden, aber ungültig |
|---|---|---|
| **headless** | Harter Startfehler, Exit-Code `1`, kein Fallback | Harter Startfehler, Exit-Code `1` |
| **GUI** | Fehlermeldung in der GUI, danach Verhalten wie ohne `--config` (Willkommenstext) | Fehlermeldung in der GUI, Konfiguration nicht geladen |

Im headless Betrieb ist ein nicht vorhandener `--config`-Pfad ein **harter Startfehler**. Ein stiller
Fallback auf das Default-Verhalten ist in diesem Fall ausdrücklich unzulässig.

### Verhalten bei GUI-Startfehlern

Tritt vor der erfolgreichen Anzeige der grafischen Oberfläche ein nicht behebbarer Fehler auf
(z. B. fehlende JavaFX-Laufzeit, Bootstrap-Fehler), beendet sich die Anwendung mit Exit-Code `1`.

### Plattform und Laufwerksbuchstaben

Die GUI wird **offiziell nur unter Windows** unterstützt. Der headless Betrieb bleibt für den
Windows Server-Betrieb geeignet.

Gemappte Netzlaufwerke wie `S:\` oder `H:\` werden ausdrücklich unterstützt. Eine Ablehnung
solcher Pfade allein wegen eines dahinterliegenden UNC-Pfads ist unzulässig.

### Startverhalten der GUI

Die GUI startet **maximiert** (Vollbild). Beim Start wird die zuletzt geladene
Konfigurationsdatei automatisch geladen. Der Pfad wird in den Windows-Benutzereinstellungen
gespeichert (`java.util.prefs.Preferences`). Existiert die Datei beim nächsten Start nicht
mehr, startet die GUI ohne Fehlermeldung mit dem Willkommenstext.

### Umfang der GUI

Die GUI enthält fünf Tabs:

- **Tab „Konfiguration"** – Editor, Validierungs- und technische Testoberfläche für
  die `.properties`-Datei (Erreichbarkeit des Providers, Pfade, SQLite-Datei,
  Prompt-Datei).
- **Tab „Verarbeitungslauf"** – Start eines Batch-Laufs aus der GUI mit
  Live-Fortschritt, Ergebnisliste und KI-Begründung je Dokument. Pro Zeile ist eine
  **integrierte PDF-Vorschau** der Quelldatei sowie ein **editierbarer Dateiname-Bereich**
  verfügbar. Der Lauf verwendet den zuletzt gespeicherten Stand der `.properties`-Datei;
  ungespeicherte Änderungen im Editor fließen nicht in den Lauf ein. Ein **Soft-Stop**
  über den Abbrechen-Knopf beendet den Lauf nach Abschluss der gerade bearbeiteten Datei.
  Während eines laufenden Batches ist Tab 1 gesperrt; ein Hinweis weist darauf hin.
- **Tab „Scheduler"** – Optionaler automatischer Scheduler für periodische Verarbeitungsläufe.
  Kann gestartet, gestoppt und mit einem konfigurierten Intervall betrieben werden. Während
  der Scheduler aktiv ist, sind Tab 1 „Konfiguration" und der manuelle Lauf gesperrt.
  Erfordert `scheduler.enabled=true` und ein gültiges `scheduler.interval.seconds` in der
  gespeicherten Konfiguration.
- **Tab „Verlauf"** – Ansicht aller bisher verarbeiteten Dokumente mit Status, Dateinamen
  und Verarbeitungsdetails direkt aus der SQLite-Datenbank. Ermöglicht Status-Reset und
  Löschung einzelner Einträge.
- **Tab „Prompt"** – Lädt, bearbeitet und speichert die konfigurierte Prompt-Datei direkt
  aus der Oberfläche. Bearbeitungen erzeugen einen Dirty-State (Asterisk im Tab-Titel).
  Speichern erfolgt **atomar** (Temp-Datei im selben Verzeichnis + `ATOMIC_MOVE`).
  Ein „Auf Standard zurücksetzen"-Button befüllt die TextArea mit der Standard-Vorlage,
  ohne zu speichern. Fehlt die Prompt-Datei am konfigurierten Pfad, wird ein
  „Standard-Prompt erstellen"-Button angezeigt. Der Tab wird beim ersten Öffnen automatisch
  geladen. Tab-Wechsel mit ungespeicherten Änderungen löst einen Bestätigungsdialog aus.

Der headless Betrieb über den Windows Task Scheduler bleibt unverändert bestehen und
kann weiterhin für automatisierte Läufe genutzt werden. Pro Anwendungsinstanz ist genau
ein Verarbeitungslauf gleichzeitig zulässig; ein gleichzeitiger externer headless Lauf
wird jedoch nicht technisch erkannt oder blockiert.

### Automatischer Scheduler

Der GUI-Tab „Scheduler" ermöglicht den Betrieb eines optionalen, periodisch laufenden
Schedulers, der automatisch Verarbeitungsläufe anstößt.

**Konfigurationsparameter:**

| Parameter | Beschreibung | Standard |
|---|---|---|
| `scheduler.enabled` | Scheduler im GUI-Modus aktivieren (`true`/`false`); wird im headless Betrieb ignoriert | `false` |
| `scheduler.interval.seconds` | Intervall zwischen automatischen Läufen in Sekunden (Integer >= 30; Pflicht wenn `scheduler.enabled=true`); wird im headless Betrieb ignoriert | – |

Ungültige Werte (kein Integer, < 30 oder leer bei `scheduler.enabled=true`) verhindern den
Scheduler-Start und werden im GUI-Tab als Fehler gemeldet.

**Autostart:** Ist `scheduler.enabled=true` in der gespeicherten Konfiguration, startet der
Scheduler automatisch, wenn die Konfiguration beim GUI-Start geladen wird. Der erste
Verarbeitungslauf beginnt **unmittelbar** nach dem Scheduler-Start (kein initiales Warten).

**Headless-Betrieb:** Im headless Betrieb werden `scheduler.enabled` und
`scheduler.interval.seconds` vollständig ignoriert. Der Scheduler ist ausschließlich im
GUI-Modus verfügbar.

**Sperrverhalten:** Solange der Scheduler aktiv ist, ist Tab 1 „Konfiguration" gesperrt
(Bearbeitungssperre mit Hinweisbanner). Manuelles Starten eines Laufs ist ebenfalls nicht
möglich. Nach dem Stoppen des Schedulers werden beide Sperren automatisch aufgehoben.

**Schließen der Anwendung:** Versucht der Benutzer das Fenster zu schließen, während der
Scheduler aktiv ist oder ein Lauf läuft, erscheint ein Informationsdialog. Das Schließen
wird blockiert, bis der Scheduler gestoppt und kein Lauf mehr aktiv ist.

---

## Voraussetzungen

- Zugang zu einem KI-Dienst (API-Schlüssel erforderlich; unterstützte Provider: OpenAI-kompatibel, Anthropic Claude)
- Quellordner mit OCR-verarbeiteten PDF-Dateien
- Schreibzugriff auf Zielordner und Datenbankverzeichnis

### Java-Laufzeitumgebung

- Bei Verwendung des **Shade-JAR** direkt: **Java 21 JRE** auf dem Zielsystem erforderlich.
- Bei Verwendung des **Windows-Installers (V3.0)**: **keine** separate Java-Installation notwendig –
  die JRE 21 ist in der installierten Anwendung eingebettet.

---

## Start des ausführbaren JAR

Das ausführbare JAR wird durch den Maven-Build im Verzeichnis
`pdf-umbenenner-bootstrap/target/` erzeugt:

```
java -jar pdf-umbenenner-bootstrap/target/pdf-umbenenner-bootstrap-0.0.1-SNAPSHOT.jar
```

Ohne weitere Argumente öffnet sich die **GUI** (Standardstart ab V2.0).

Für den **headless Betrieb** (Batch-/Scheduler-Start):

```
java -jar pdf-umbenenner-bootstrap/target/pdf-umbenenner-bootstrap-0.0.1-SNAPSHOT.jar --headless
```

Die Anwendung liest die Konfiguration standardmäßig aus `config/application.properties` relativ zum
Arbeitsverzeichnis, in dem der Befehl ausgeführt wird.

### Konsolen-Encoding unter Windows

Die Anwendung schreibt alle Log-Ausgaben in UTF-8. Windows-Konsolen (PowerShell, CMD) verwenden
standardmäßig den OEM-Codepage (z. B. CP850), was zu unlesbaren Sonderzeichen führt.

**Lösung:** Konsole vor dem Start auf UTF-8 umschalten:

```
chcp 65001
java -jar pdf-umbenenner-bootstrap-*.jar --headless
```

Alternativ kann die UTF-8-Ausgabe auch als JVM-Argument angegeben werden (Java 17+):

```
java -Dstdout.encoding=UTF-8 -jar pdf-umbenenner-bootstrap-*.jar --headless
```

> **Hinweis:** Die mitgelieferten Batch-Dateien (`PDF-KI-Renamer.bat`, `PDF-KI-Renamer-GUI.bat`)
> rufen `chcp 65001` automatisch auf. Der Windows Task Scheduler schreibt Log-Ausgaben in eine
> Protokolldatei, die stets UTF-8-kodiert ist – dort entsteht kein Anzeigeproblem.

### Start über Windows Task Scheduler

Empfohlene Startsequenz für den headless Betrieb über den Windows Task Scheduler:

1. Aktion: Programm/Skript starten
2. Programm: `java`
3. Argumente: `-jar C:\Pfad\zur\Installation\pdf-umbenenner-bootstrap\target\pdf-umbenenner-bootstrap-0.0.1-SNAPSHOT.jar --headless`
4. Starten in: `C:\Pfad\zur\Installation` (muss das Verzeichnis mit `config\application.properties` und `config\prompts\` enthalten)

> **Hinweis:** Das „Starten in"-Verzeichnis ist das Arbeitsverzeichnis der Anwendung.
> Die Konfigurationsdatei `config/application.properties` sowie das Prompt-Verzeichnis
> `config/prompts/` müssen relativ zu diesem Verzeichnis erreichbar sein. Der JAR-Pfad
> in den Argumenten muss absolut oder relativ zum Starten-in-Verzeichnis korrekt angegeben sein.

Alternativ kann über `--config` ein expliziter Konfigurationspfad angegeben werden:

```
java -jar ... --headless --config S:\Betrieb\meine-config.properties
```

> **Wichtig:** Zeigt `--config` auf eine nicht vorhandene Datei, bricht die Anwendung mit Exit-Code `1` ab.
> Es findet kein stiller Fallback auf `config/application.properties` statt.

---

## Konfiguration

Die Konfiguration wird aus `config/application.properties` geladen.

Ein vollständiges Konfigurationsbeispiel mit allen unterstützten Parametern,
realistischen Windows-Pfaden und erklärenden Kommentaren liegt unter:

- [`docs/examples/application.properties`](examples/application.properties)

Vorlagen für lokale und Test-Konfigurationen befinden sich in:

- `config/application-local.example.properties`
- `config/application-test.example.properties`

### Pflichtparameter (allgemein)

| Parameter               | Beschreibung |
|-------------------------|--------------|
| `source.folder`         | Quellordner mit OCR-PDFs (muss vorhanden und lesbar sein) |
| `target.folder`         | Zielordner für umbenannte Kopien (wird angelegt, wenn nicht vorhanden) |
| `sqlite.file`           | SQLite-Datenbankdatei (übergeordnetes Verzeichnis muss existieren) |
| `ai.provider.active`    | Aktiver KI-Provider: `openai-compatible` oder `claude` |
| `max.retries.transient` | Maximale transiente Fehlversuche pro Dokument (ganzzahlig, >= 1) |
| `max.pages`             | Maximale Seitenzahl pro Dokument (ganzzahlig, > 0) |
| `max.text.characters`   | Maximale Zeichenanzahl des Dokumenttexts für KI-Anfragen (ganzzahlig, > 0) |
| `max.title.length`      | Maximale Länge des Basistitels in Zeichen (ganzzahlig, 10..120, Default 60). Werte unter 10 oder über 120 verhindern den Start. Werte 10–39 und 100–120 erzeugen eine Startwarnung. |
| `prompt.template.file`  | Pfad zur externen Prompt-Datei (muss vorhanden sein) |

### Provider-Parameter

Nur der **aktive** Provider muss vollständig konfiguriert sein. Der inaktive Provider wird nicht validiert.

**OpenAI-kompatibler Provider** (`ai.provider.active=openai-compatible`):

| Parameter | Beschreibung |
|-----------|--------------|
| `ai.provider.openai-compatible.baseUrl` | Basis-URL des KI-Dienstes (z. B. `https://api.openai.com/v1`) |
| `ai.provider.openai-compatible.model` | Modellname (z. B. `gpt-4o-mini`) |
| `ai.provider.openai-compatible.timeoutSeconds` | HTTP-Timeout in Sekunden (ganzzahlig, > 0) |
| `ai.provider.openai-compatible.apiKey` | API-Schlüssel (Umgebungsvariable `OPENAI_COMPATIBLE_API_KEY` hat Vorrang) |

**Anthropic Claude-Provider** (`ai.provider.active=claude`):

| Parameter | Beschreibung |
|-----------|--------------|
| `ai.provider.claude.baseUrl` | Basis-URL (optional; Standard: `https://api.anthropic.com`) |
| `ai.provider.claude.model` | Modellname (z. B. `claude-3-5-sonnet-20241022`) |
| `ai.provider.claude.timeoutSeconds` | HTTP-Timeout in Sekunden (ganzzahlig, > 0) |
| `ai.provider.claude.apiKey` | API-Schlüssel (Umgebungsvariable `ANTHROPIC_API_KEY` hat Vorrang) |

### Optionale Parameter

| Parameter           | Beschreibung | Standard |
|---------------------|--------------|---------|
| `runtime.lock.file` | Lock-Datei für Startschutz | `pdf-umbenenner.lock` im Arbeitsverzeichnis |
| `log.directory`     | Log-Verzeichnis | `./logs/` |
| `log.level`         | Log-Level (`DEBUG`, `INFO`, `WARN`, `ERROR`) | `INFO` |
| `log.ai.sensitive`  | KI-Rohantwort und Reasoning ins Log schreiben (`true`/`false`) | `false` |
| `scheduler.enabled` | Scheduler im GUI-Modus aktivieren (`true`/`false`); wird im headless Betrieb ignoriert | `false` |
| `scheduler.interval.seconds` | Intervall in Sekunden (Integer >= 30; Pflicht wenn `scheduler.enabled=true`); wird im headless Betrieb ignoriert | – |

### API-Schlüssel

Pro Provider-Familie existiert eine eigene Umgebungsvariable, die Vorrang vor dem Properties-Wert hat:

| Provider | Umgebungsvariable |
|---|---|
| `openai-compatible` | `OPENAI_COMPATIBLE_API_KEY` |
| `claude` | `ANTHROPIC_API_KEY` |

Schlüssel verschiedener Provider-Familien werden niemals vermischt.

---

## Migration älterer Konfigurationsdateien

Ältere Konfigurationsdateien, die noch die flachen Schlüssel `api.baseUrl`, `api.model`,
`api.timeoutSeconds` und `api.key` verwenden, werden beim ersten Start **automatisch**
in das aktuelle Schema überführt.

### Was passiert

1. Die Anwendung erkennt die veraltete Form anhand der flachen `api.*`-Schlüssel.
2. **Vor jeder Änderung** wird eine Sicherungskopie der Originaldatei angelegt:
   - Standardfall: `config/application.properties.bak`
   - Falls `.bak` bereits existiert: `config/application.properties.bak.1`, `.bak.2`, …
   - Bestehende Sicherungen werden **niemals überschrieben**.
3. Die Datei wird in-place in das neue Schema überführt:
   - `api.baseUrl` → `ai.provider.openai-compatible.baseUrl`
   - `api.model` → `ai.provider.openai-compatible.model`
   - `api.timeoutSeconds` → `ai.provider.openai-compatible.timeoutSeconds`
   - `api.key` → `ai.provider.openai-compatible.apiKey`
   - `ai.provider.active=openai-compatible` wird ergänzt.
   - Alle übrigen Schlüssel bleiben unverändert.
4. Die migrierte Datei wird über eine temporäre Datei (`*.tmp`) und atomischen
   Move/Rename geschrieben. Das Original wird niemals teilbeschrieben.
5. Die migrierte Datei wird sofort neu eingelesen und validiert.

### Bei Migrationsfehler

Schlägt die Validierung der migrierten Datei fehl, bricht die Anwendung mit Exit-Code `1` ab.
Die Sicherungskopie (`.bak`) bleibt in diesem Fall erhalten und enthält die unveränderte
Originaldatei. Die Konfiguration muss dann manuell korrigiert werden.

### Betreiber-Hinweis

Die Umgebungsvariable `PDF_UMBENENNER_API_KEY` des Vorgängerstands wird **nicht** automatisch
umbenannt. Falls dieser Wert bislang verwendet wurde, muss er auf `OPENAI_COMPATIBLE_API_KEY`
umgestellt werden.

---

## Prompt-Konfiguration

Der Prompt wird aus der in `prompt.template.file` konfigurierten externen Textdatei geladen.
Der Dateiname der Prompt-Datei dient als Prompt-Identifikator in der Versuchshistorie
(SQLite) und ermöglicht so die Nachvollziehbarkeit, welche Prompt-Version für welchen
Verarbeitungsversuch verwendet wurde.

Eine angepasste Vorlage befindet sich in `config/prompts/template.txt` und kann direkt
verwendet oder an den jeweiligen KI-Dienst angepasst werden.

Fehlt die konfigurierte Prompt-Datei, erzeugt die GUI beim Ausführen der technischen Tests
automatisch eine deutsche Standardvorlage am konfigurierten Pfad. Ein Beispiel dieser
Standardvorlage liegt unter [`docs/examples/prompt.txt`](examples/prompt.txt).

Die Anwendung ergänzt den Prompt automatisch um:
- einen Dokumenttext-Abschnitt
- eine explizite JSON-Antwortspezifikation mit den Feldern `title`, `reasoning` und `date`

### Prompt-Pfad-Auflösung je Betriebsart

Der Wert von `prompt.template.file` wird **relativ zum Arbeitsverzeichnis** aufgelöst,
wenn kein absoluter Pfad angegeben ist. Das Arbeitsverzeichnis hängt von der Betriebsart ab:

| Betriebsart | Arbeitsverzeichnis | Empfohlener Wert |
|---|---|---|
| **IDE** | Projekt-Wurzelverzeichnis (in der Regel das Parent-POM-Verzeichnis) | `config/prompts/template.txt` |
| **Shade-JAR direkt** | Verzeichnis, aus dem `java -jar ...` aufgerufen wird | `config/prompts/template.txt` |
| **Windows Task Scheduler** | „Starten in"-Feld der Task-Konfiguration | absoluter Pfad empfohlen, z. B. `C:\Betrieb\config\prompts\template.txt` |
| **Windows-Installer (MSI)** | Installationsverzeichnis | absoluter Pfad empfohlen |

> **Empfehlung für den Windows-Produktivbetrieb:** Verwenden Sie einen **absoluten Pfad**
> für `prompt.template.file`. Damit ist die Prompt-Datei unabhängig vom Arbeitsverzeichnis
> immer eindeutig auffindbar – insbesondere beim Start über den Windows Task Scheduler,
> wo das Arbeitsverzeichnis je nach Konfiguration variieren kann.

### Bearbeitung über den GUI-Prompt-Tab

Im GUI-Tab „Prompt" kann die Prompt-Datei ohne externen Editor gelesen, bearbeitet und
gespeichert werden. Das Speichern erfolgt atomar; ein Rollback schlägt nur fehl, wenn
das Dateisystem kein atomisches Verschieben im selben Verzeichnis unterstützt (in diesem
Fall wird kein stiller Fallback durchgeführt).

Der Tab zeigt stets die Datei an, die beim GUI-Start als `prompt.template.file` konfiguriert
war. Wird während der GUI-Session eine andere `.properties`-Datei geöffnet (Tab „Konfiguration"),
aktualisiert sich der Prompt-Tab nicht automatisch – in diesem Fall sollte die GUI neu gestartet
oder der Prompt-Tab durch erneutes Auswählen manuell neu geladen werden.

---

## Zielformat

Jede erfolgreich verarbeitete PDF-Datei wird im Zielordner unter folgendem Namen abgelegt:

```
YYYY-MM-DD - Titel.pdf
```

Bei Namenskollisionen wird ein laufendes Suffix angehängt:

```
YYYY-MM-DD - Titel(1).pdf
YYYY-MM-DD - Titel(2).pdf
```

Das Suffix zählt nicht zur konfigurierten maximalen Titellänge des Basistitels.

---

## Retry- und Skip-Verhalten

### Dokumentstatus

Die folgende Tabelle beschreibt die persistenten Statuszustände der Dokument-Stammsätze
in der SQLite-Datenbank. Diese Zustände sind nach Abschluss eines Verarbeitungsversuchs
dauerhaft gespeichert.

| Status                      | Bedeutung |
|-----------------------------|-----------|
| `READY_FOR_AI`              | Verarbeitbar, KI-Pfad noch nicht durchlaufen |
| `PROPOSAL_READY`            | KI-Benennungsvorschlag liegt vor, Zielkopie noch nicht geschrieben |
| `SUCCESS`                   | Erfolgreich verarbeitet und kopiert (terminaler Endzustand) |
| `FAILED_RETRYABLE`          | Fehlgeschlagen, erneuter Versuch in späterem Lauf möglich |
| `FAILED_FINAL`              | Terminal fehlgeschlagen, wird nicht erneut verarbeitet |
| `SKIPPED_ALREADY_PROCESSED` | Übersprungen – Dokument bereits erfolgreich verarbeitet |
| `SKIPPED_FINAL_FAILURE`     | Übersprungen – Dokument terminal fehlgeschlagen |

Zusätzlich kennt das System den transienten Zustand `PROCESSING`, der während der aktiven
Verarbeitung eines Dokuments im Stammsatz gesetzt werden kann. Er wird nach Abschluss des
Verarbeitungsversuchs stets durch einen der obigen Zustände ersetzt und ist kein gültiger
Endstatus in der Datenbank.

### Retry-Regeln

**Deterministische Inhaltsfehler** (z. B. kein extrahierbarer Text, Seitenlimit überschritten,
unbrauchbarer KI-Titel):

- Erster Fehler → `FAILED_RETRYABLE` (ein Wiederholversuch in späterem Lauf erlaubt)
- Zweiter Fehler → `FAILED_FINAL` (kein weiterer Versuch)

**Transiente technische Fehler** (z. B. KI nicht erreichbar, HTTP-Timeout):

- Wiederholbar bis zum Grenzwert `max.retries.transient`
- Bei Erreichen des Grenzwerts → `FAILED_FINAL`

**Technischer Sofort-Wiederholversuch:**

Bei einem Schreibfehler der Zielkopie wird innerhalb desselben Laufs exakt ein
Sofort-Wiederholversuch unternommen. Dieser zählt nicht zum laufübergreifenden
Fehlerzähler.

---

## Logging

Logs werden in das konfigurierte `log.directory` geschrieben (Standard: `./logs/`).
Log-Rotation erfolgt täglich und bei Erreichen von 10 MB je Datei.

### Sensible KI-Inhalte

Standardmäßig werden die vollständige KI-Rohantwort und das KI-Reasoning **nicht** ins Log
geschrieben, sondern ausschließlich in der SQLite-Datenbank gespeichert.

Die Ausgabe kann für Diagnosezwecke mit `log.ai.sensitive=true` freigeschaltet werden.
Erlaubte Werte: `true` oder `false`. Jeder andere Wert ist ungültig und verhindert den Start.

---

## Exit-Codes

| Code | Bedeutung |
|------|-----------|
| `0`  | Lauf technisch ordnungsgemäß ausgeführt (auch bei dokumentbezogenen Teilfehlern) |
| `1`  | Harter Start- oder Bootstrap-Fehler (ungültige Konfiguration, Lock nicht erwerbbar, Schema-Initialisierungsfehler) |

Dokumentbezogene Fehler einzelner PDF-Dateien führen **nicht** zu Exit-Code `1`.

---

## Startschutz (Parallelinstanzschutz)

Die Anwendung verwendet eine exklusive Lock-Datei, um parallele Instanzen zu verhindern.
Wenn bereits eine Instanz läuft, beendet sich die neue Instanz sofort mit Exit-Code `1`.

Der Pfad der Lock-Datei ist über `runtime.lock.file` konfigurierbar.

### Pfadauflösung der Lock-Datei

| Pfadtyp | Verhalten |
|---|---|
| **Absoluter Pfad** | Wird direkt verwendet. Schlägt das Anlegen der Lock-Datei fehl, bricht der Start mit einer klaren Fehlermeldung ab – kein Fallback. |
| **Relativer oder unkonfigurierter Pfad** | Zweistufige Auflösung: (1) relativ zum Verzeichnis der JAR-Datei (`CodeSource.getLocation()`), (2) Fallback auf das Benutzerverzeichnis (`user.home`). Erst wenn auch `user.home` fehlschlägt, bricht der Start ab. |

Fehlende übergeordnete Verzeichnisse werden automatisch angelegt.

Der tatsächlich verwendete absolute Pfad der Lock-Datei wird beim Start auf INFO-Level geloggt, z. B.:

```
Lock-Datei: C:\Users\Funny\Documents\pdf-umbenenner.lock
```

Diese Auflösungslogik gilt sowohl für den GUI- als auch für den headless Start.

> **Empfehlung für den MSI-Betrieb:** Da das Installationsverzeichnis `C:\Program Files\`
> schreibgeschützt ist, muss `runtime.lock.file` als absoluter Pfad auf ein beschreibbares
> Verzeichnis zeigen (z. B. `C:/ProgramData/PDF KI Renamer/pdf-umbenenner.lock`).

---

## SQLite-Datenbank

Die SQLite-Datei enthält:

- **Dokument-Stammsätze**: Gesamtstatus, Fehlerzähler, letzter Zieldateiname, Zeitstempel
- **Versuchshistorie**: Jeder Verarbeitungsversuch mit Modell, Prompt-Identifikator,
  KI-Rohantwort, Reasoning, Datum, Titel, Fehlerstatus und Fehlerdetails

Die Datenbank ist die führende Wahrheitsquelle für Bearbeitungsstatus und Nachvollziehbarkeit.
Sie muss nicht manuell verwaltet werden – das Schema wird beim Start automatisch initialisiert.

### Fehlerursache im Verlauf-Tab

Verarbeitungsversuche mit Status `FAILED_FINAL`, `FAILED_RETRYABLE` oder
`SKIPPED_FINAL_FAILURE` speichern eine nutzerverständliche Fehlerursache
(`failure_details`). Diese wird im Verlauf-Tab im Detailbereich des jeweiligen
Dokuments angezeigt. Ältere Einträge ohne Fehlerdetails zeigen einen Platzhaltertext.
Fehlerdetails werden auf 1000 Zeichen begrenzt und enthalten keine rohen
Provider-Meldungen oder API-Schlüssel.

### Neue Datenbank anlegen

Über den Menüpunkt **Datenbank → Neue Datenbank anlegen...** kann aus der GUI
heraus eine neue, leere SQLite-Datenbank erstellt und sofort aktiviert werden,
ohne die Anwendung neu zu starten.

**Ablauf:**

1. Dateidialog öffnet (Filter: `*.sqlite` und `*.db`); Zieldatei wählen oder eingeben.
2. Sicherheitsprüfung: aktive und gewählte Datei werden normalisiert verglichen
   (case-insensitive unter Windows). Bei Übereinstimmung erscheint eine Fehlermeldung.
3. Bei bereits existierender Fremddatei: Bestätigungsdialog „Die Datei existiert bereits.
   Überschreiben?"
4. Neue SQLite-Datei wird als temporäre Datei erzeugt, Flyway führt alle Migrationsskripte
   auf neuesten Stand aus, dann Verbindungstest.
5. Nach erfolgreichem Test: atomarer Move zur Zieldatei.
6. Aktive Datenbankverbindung der Anwendung wechselt zur neuen DB.
7. Der Verlauf-Tab lädt neu und zeigt „Noch keine Verarbeitungen vorhanden."
8. Die Statuszeile aktualisiert den DB-Pfad.

> **Wichtig:** Die Konfigurationsdatei wird durch den Wechsel automatisch als geändert
> markiert. **Konfiguration speichern**, damit die neue Datenbank beim nächsten Start
> der Anwendung verwendet wird.

**Fehlerfall:** Schlägt ein Schritt fehl, bleibt die bisherige Datenbank unverändert
in Betrieb. Die temporäre Datei wird gelöscht. Ein Fehlerdialog erscheint.

Der Menüpunkt ist nur aktiv, wenn kein Verarbeitungslauf läuft.
Der headless Betrieb ist von dieser Funktion nicht betroffen.

---

## Build und Packaging

### Gemeinsames ausführbares JAR

Die gesamte Anwendung wird als **ein einziges ausführbares JAR** ausgeliefert, das GUI-Start
und headless Batch-Betrieb vereint. Eine separate JavaFX-Installation ist nicht erforderlich.

Das JAR wird vom Maven-Shade-Plugin im Modul `pdf-umbenenner-bootstrap` erzeugt.
Nach einem erfolgreichen Build liegt es unter:

```
pdf-umbenenner-bootstrap/target/pdf-umbenenner-bootstrap-0.0.1-SNAPSHOT.jar
```

Dieses JAR enthält alle Abhängigkeiten inklusive der JavaFX-Plattformbibliotheken
für Windows (Classifier `win`). Die nativen JavaFX-DLLs werden beim GUI-Start
von JavaFX selbst in ein temporäres Verzeichnis extrahiert.

### Integrierte JavaFX-Laufzeit

JavaFX ist als Maven-Dependency im Modul `pdf-umbenenner-adapter-in-gui` mit
Windows-Classifier deklariert (`javafx-base:win`, `javafx-graphics:win`,
`javafx-controls:win`). Das Shade-JAR schließt diese Bibliotheken ein, sodass
der GUI-Start ohne separate JavaFX-Installation auf dem Zielsystem funktioniert.

Nur das Modul `pdf-umbenenner-adapter-in-gui` hängt direkt von JavaFX ab.
Die Module `domain`, `application`, `adapter-in-cli` und `adapter-out` sind
vollständig JavaFX-frei.

### Headless-Start ohne JavaFX-Initialisierung

Beim headless Start (`--headless`) wird JavaFX **nicht** initialisiert. Der
`GuiAdapter` wird nur dann instanziiert und gestartet, wenn der Startmodus GUI ist.
JavaFX-Klassen sind zwar im Shade-JAR enthalten, werden im headless Pfad jedoch
nicht geladen. Headless läuft damit auch auf Windows Server-Systemen ohne
JavaFX-fähige Grafiklaufzeit.

### Windows-Installer (V3.0)

Ab V3.0 steht neben dem Shade-JAR ein vollwertiger **MSI-Installer** für Windows 10/11 (x64)
und Windows Server 2022 (x64) bereit. Der Installer enthält eine eingebettete JRE 21 und
benötigt keine separate Java-Installation auf dem Zielsystem. Das Shade-JAR bleibt das
primäre Distributionsartefakt; der MSI ist eine zusätzliche Option für Systeme ohne
Java-Installation und für den Standard-Installationspfad nach `C:\Program Files\`.

> **Hinweis zur CI-Umgebung:** Der MSI-Build ist Windows-only (`jpackage` + WiX Toolset 3.x).
> Jenkins läuft im Linux-Container auf dem Synology NAS und kann kein MSI erzeugen.
> Der MSI-Build wird bewusst manuell auf der Windows-Entwicklungsmaschine ausgeführt.

**Voraussetzungen für den Installer-Build (nur auf der Entwicklungsmaschine):**
- Windows x64
- JDK 21 im PATH
- [WiX Toolset 3.x](https://wixtoolset.org/) im PATH

**MSI bauen:**

```powershell
.\mvnw.cmd clean package -P release -pl pdf-umbenenner-packaging --also-make -DskipTests
```

Der normale Build (`mvn clean verify`) ist vom Profil `release` vollständig unberührt
und benötigt **kein** WiX Toolset.

Das Ergebnis liegt unter:

```
pdf-umbenenner-packaging/target/dist/
  PDF-KI-Renamer-2.5.0.msi   ← Windows-Installer
  PDF-KI-Renamer.bat         ← Headless-Start (zusätzlich kopiert)
  PDF-KI-Renamer-GUI.bat     ← GUI-Start (zusätzlich kopiert)
```

**Installationsverzeichnis:**

Der Installer legt die Anwendung nach `C:\Program Files\PDF KI Renamer\` ab.
Beide Batch-Dateien landen ebenfalls dort. Der Installer erstellt:
- einen Startmenü-Eintrag in der Gruppe `PDF KI Renamer` (startet die GUI)
- einen Desktop-Shortcut (startet die GUI)

Die Deinstallation erfolgt über „Programme und Features" in der Windows-Systemsteuerung.
Vom Installer angelegte Dateien werden entfernt; Nutzerdaten unter `C:\ProgramData\PDF KI Renamer\`
(Konfiguration, Logs, SQLite-Datenbank) bleiben erhalten.

**Konfigurationsverzeichnis (`ProgramData`):**

Das empfohlene Konfigurationsverzeichnis für den produktiven Betrieb ist:

```
C:\ProgramData\PDF KI Renamer\config\
```

Die Anwendung löst dieses Verzeichnis **nicht** automatisch auf. Der Pfad zur
Konfigurationsdatei muss weiterhin explizit über `--config` angegeben werden
(siehe „CLI-Optionen"). Der Installer legt eine Beispiel-Konfiguration namens
`application.example.properties` neben den installierten Artefakten im
Installationsverzeichnis ab. **Der Betreiber muss diese Beispieldatei manuell nach**
`C:\ProgramData\PDF KI Renamer\config\` **kopieren und anpassen.**

**Beispielaufruf headless mit installierter Anwendung:**

```powershell
"C:\Program Files\PDF KI Renamer\PDF-KI-Renamer.bat" --config "C:\ProgramData\PDF KI Renamer\config\application.properties"
```

**Hinweis:** Der MSI ist nicht signiert. Beim Installieren erscheint eine
Windows-SmartScreen-Warnung, die durch „Weitere Informationen → Trotzdem ausführen"
bestätigt werden muss. Code-Signing ist für spätere Ausbaustufen vorgesehen.

**Empfehlung für Pfade im MSI-Betrieb:**

Für den MSI-Betrieb (Startmenü, Task Scheduler) müssen alle Dateipfade als **absolute Pfade**
konfiguriert werden. Relative Pfade werden relativ zum Installationsverzeichnis
`C:\Program Files\PDF KI Renamer\` aufgelöst, das **schreibgeschützt** ist. Dadurch
schlagen Schreibversuche (Logs, SQLite-Datenbank, Lock-Datei) ohne Fehlermeldung fehl.

> **Warnung – Relative Pfade im MSI-Betrieb nicht verwenden:**
> Pfade wie `./logs`, `./work/local/logs` oder `logs/` werden im MSI-Betrieb relativ
> zum Installationsverzeichnis aufgelöst. Das Installationsverzeichnis ist für normale
> Benutzerkonten schreibgeschützt. Log4j2 scheitert dann still, ohne eine sichtbare
> Fehlermeldung zu erzeugen.

> **Warnung – Backslashes in `.properties`-Dateien:**
> In Java-`.properties`-Dateien werden Backslashes (`\`) als Escape-Zeichen interpretiert.
> Windows-Pfade wie `C:\Users\Funny\Logs` müssen entweder mit Forward-Slashes
> (`C:/Users/Funny/Logs`) oder mit doppelten Backslashes (`C:\\Users\\Funny\\Logs`)
> angegeben werden. Einfache Backslashes werden stillschweigend falsch interpretiert.

Betroffene Parameter:

| Parameter | Empfehlung |
|---|---|
| `log.directory` | Absoluter Pfad, z. B. `C:/ProgramData/PDF KI Renamer/logs` |
| `runtime.lock.file` | Absoluter Pfad, z. B. `C:/ProgramData/PDF KI Renamer/pdf-umbenenner.lock` |
| `prompt.template.file` | Absoluter Pfad, z. B. `C:/ProgramData/PDF KI Renamer/config/prompts/template.txt` |
| `sqlite.file` | Absoluter Pfad, z. B. `C:/ProgramData/PDF KI Renamer/config/pdf-umbenenner.db` |

Das empfohlene Konfigurationsverzeichnis für alle schreibbaren Daten im MSI-Betrieb ist
`C:\ProgramData\PDF KI Renamer\`, da dieses Verzeichnis standardmäßig für alle
Benutzerkonten schreibbar ist und bei der Deinstallation erhalten bleibt.

**Diagnose: Log-Datei-Prüfpunkt in den technischen Tests**

Die technischen Tests (Schaltfläche „Technische Tests ausführen" im Konfigurationseditor)
enthalten einen dedizierten Prüfpunkt **„Log-Verzeichnis beschreibbar"**, der anzeigt:

- den konfigurierten `log.directory`-Wert (roh und als aufgelöster absoluter Pfad),
- ob das Verzeichnis vorhanden und beschreibbar ist,
- den tatsächlichen Log-Dateipfad aus der laufenden Log4j2-Konfiguration.

Ein nicht beschreibbares Log-Verzeichnis wird als **Warnung** angezeigt, nicht als Fehler
(die Anwendung kann ohne Datei-Logging laufen). Der Prüfpunkt hilft, den typischen
MSI-Betriebsfehler – relatives `log.directory` auf schreibgeschütztem Installationspfad –
frühzeitig zu erkennen.

### MSI-Release-Checkliste

Die folgende Checkliste ist vor jeder MSI-Auslieferung manuell abzuarbeiten.

- [ ] Neuinstallation auf sauberer Windows-Umgebung ohne vorinstalliertes Java
- [ ] Installation in Installationspfad **mit Leerzeichen** (z. B. `C:\Program Files\PDF KI Renamer\`)
- [ ] Upgrade von installiertem Vorgänger-MSI (kein manuelles Deinstallieren)
- [ ] GUI-Start über Startmenü-Eintrag
- [ ] Headless-Start über `PDF-KI-Renamer.bat` im Windows Task Scheduler
- [ ] Desktop-Shortcut vorhanden oder Einschränkung hier dokumentiert
- [ ] App-Version `3.0.x` im Windows-Installer sichtbar („Programme und Features")
- [ ] Deinstallation sauber – Konfiguration unter `C:\ProgramData\PDF KI Renamer\` bleibt erhalten
- [ ] SmartScreen-Warnung erscheint und wird durch „Weitere Informationen → Trotzdem ausführen" bestätigt
- [ ] BAT-Dateien funktionieren bei Installationspfad mit Leerzeichen
- [ ] Anwendungsstart **ohne Entwicklungs-JDK** erfolgreich: GUI-Start, PDF laden und rendern, Verarbeitungslauf starten, Verlaufs-Tab öffnen (Verifikation der `addModules`-Liste)

> **Hinweis zur JDK-freien Laufzeit-Verifikation:** Nur ein erfolgreicher Test
> auf einem System ohne installiertes JDK bestätigt die Vollständigkeit der
> `addModules`-Liste in `pdf-umbenenner-packaging/pom.xml`. Die aktuelle Liste
> wurde per `jdeps --print-module-deps --ignore-missing-deps` ermittelt;
> vollständige Ausgabe in `pdf-umbenenner-packaging/jdeps-output.txt`.

### Build-Kommandos

**Vollständiger Reactor-Build** (alle Module, Tests, Packaging):

```powershell
.\mvnw.cmd clean verify
```

Auf Unix-Systemen (headless CI):

```bash
./mvnw clean verify
```

**Nur das ausführbare JAR erzeugen** (überspringt Tests):

```powershell
.\mvnw.cmd clean package -pl pdf-umbenenner-bootstrap --also-make -DskipTests
```

**Selektiver Reactor-Build** (ohne Coverage-Modul, z. B. während der Entwicklung):

```powershell
.\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
```

### Technische Hinweise zum Shade-JAR

- Signaturdateien (`*.SF`, `*.DSA`, `*.RSA`) signierter JARs (u. a. JavaFX) werden
  beim Shading entfernt, da sie im zusammengeführten JAR ungültig wären.
- JPMS-Moduldeskriptoren (`module-info.class`) werden entfernt, da JavaFX als
  modulares Framework mit dem nicht-modularen Fat-JAR-Modell kollidieren würde.
- `META-INF/services`-Einträge aus allen Abhängigkeiten werden durch den
  `ServicesResourceTransformer` zusammengeführt statt überschrieben.
- Der Main-Class-Eintrag im Manifest verweist auf
  `de.gecheckt.pdf.umbenenner.bootstrap.PdfUmbenennerApplication`.
  Diese Klasse erweitert bewusst **nicht** `javafx.application.Application`,
  um den JavaFX-Modul-System-Launcher-Check zu umgehen, der Fat-JAR-Ausführung
  blockieren würde. Der GUI-Pfad ruft `Application.launch(...)` explizit auf.

---

## GUI: Selektive Wiederverarbeitung und Status-Reset

Die GUI ermöglicht nach Abschluss eines Verarbeitungslaufs zwei zusätzliche Aktionen auf der Ergebnisliste:

### Selektion in der Ergebnisliste

Die Ergebnisliste enthält eine **Checkbox pro Zeile** sowie eine **Master-Checkbox** zum Auswählen aller Einträge.
- Auswahl erfolgt wie im Windows Explorer mit **Shift/Strg-Mehrfachselektion**
- Alle vier Statustypen sind selektierbar: erfolgreich, retryable, permanent fehlgeschlagen, übersprungen
- Während eines Laufs ist die Selektion **gesperrt**

### Button „Erneut verarbeiten"

**Aktion:** DB-Status zurücksetzen + sofortiger Mini-Lauf nur für ausgewählte Dateien.

- Aktiv nur wenn kein Lauf läuft und mindestens 1 Eintrag selektiert ist
- Der Mini-Lauf arbeitet auf einem Snapshot der beim Klick ausgewählten Einträge
- Nicht ausgewählte Einträge bleiben unverändert in der Liste
- Verhalten identisch zu regulärem Lauf (gleiche Anwendungslogik, nur eingeschränkte Dateimenge)

**Besonderheit bei identischem Zieldateinamen:** Verarbeitet der KI-Provider wieder denselben Dateinamen wie ein vorangegangener erfolgreicher Lauf, erhält der Eintrag **Status erfolgreich** – es wird keine erneute Kopie erzeugt, kein Fehler.

**Fehlende Quelldatei:** Ist die Datei zum Zeitpunkt des Mini-Laufs nicht mehr vorhanden, erhält der Eintrag **Status permanent fehlgeschlagen** mit Meldung „Quelldatei nicht gefunden".

### Button „Status zurücksetzen"

**Aktion:** Nur DB-Status zurücksetzen, keine sofortige Verarbeitung.

- Aktiv nur wenn kein Lauf läuft und mindestens 1 Eintrag selektiert ist
- Betroffene Zeilen erhalten die Kennzeichnung **„Zurückgesetzt – wartet auf nächsten Lauf"**
- Beim nächsten regulären Lauf werden zurückgesetzte Dateien automatisch mitgenommen
- **Best-effort-Reset:** Erfolgreiche und fehlgeschlagene Resets werden pro Eintrag einzeln durchgeführt; Zusammenfassung zeigt Erfolge und Fehler

### Verhalten während eines Mini-Laufs

- Der **Abbrechen-Button** gilt auch für Mini-Läufe (Soft-Stop)
- **Tab 1 „Konfiguration" ist während des Mini-Laufs gesperrt**
- Nach Soft-Stop: bereits verarbeitete Einträge behalten neuen Status, noch nicht gestartete zurückgesetzte Einträge warten auf nächsten regulären Lauf
- Fortschrittsbalken zeigt Fortschritt für die ausgewählte Dateimenge

---

## Weitere Dokumentation

Die Bedienung der GUI ist in [`gui-bedienanleitung.md`](gui-bedienanleitung.md) beschrieben.

---

## Systemgrenzen

- Nur OCR-verarbeitete, durchsuchbare PDF-Dateien werden verarbeitet
- Keine eingebaute OCR-Funktion
- Kein Web-UI, keine REST-API
- Die GUI ermöglicht Konfiguration, Validierung, technische Diagnose und die Ausführung von Verarbeitungsläufen mit integrierter PDF-Vorschau und editierbarem Dateiname
- Kein interner Scheduler im headless Betrieb – der Batch-Betrieb wird extern angestoßen (z. B. Windows Task Scheduler, `--headless`); im GUI-Modus steht optional ein interner Scheduler zur Verfügung (Tab „Scheduler")
- Quelldateien werden nie überschrieben, verschoben oder gelöscht
- Die Identifikation erfolgt über SHA-256-Fingerprint des Dateiinhalts, nicht über Dateinamen
- Die GUI wird offiziell nur unter Windows unterstützt; der headless Betrieb ist für Windows Server geeignet
