# V1.1 – Arbeitspakete

> **Aktive Erweiterung:** Zusätzliche KI-Provider-Familie **Anthropic Claude** über die native Messages API, neben der bestehenden OpenAI-kompatiblen Anbindung. Bewusst minimale Erweiterung des freigegebenen Basisstands.

> **Ablage im Repository:** `docs/workpackages/V1.1 - Arbeitspakete.md`

---

## 0. Lesereihenfolge für jedes Arbeitspaket

Vor jedem AP **vollständig** lesen:
1. `CLAUDE.md`
2. `docs/specs/technik-und-architektur.md`
3. `docs/specs/fachliche-anforderungen.md`
4. dieses Dokument: Abschnitte 1 bis 6
5. **nur** das aktive Arbeitspaket aus Abschnitt 7

Nicht vorgreifen. Nicht raten. Bei echter Unklarheit knapp benennen statt erfinden.

---

## 1. Arbeitsweise (verbindlich)

Diese Regeln ersetzen die nicht vorhandene `WORKFLOW.md` und gelten für alle APs in diesem Dokument.

### 1.1 Scope-Disziplin
- Es wird **ausschließlich** das aktive Arbeitspaket umgesetzt.
- Keine Inhalte späterer Arbeitspakete vorwegnehmen.
- Keine kosmetischen Refactorings ohne direkten Bezug zum AP.
- Keine Umbenennungen außerhalb des AP-Scopes.
- Vor Änderungen die betroffenen Klassen über Typsuche im Repo lokalisieren, **nicht** über vermutete Pfade.

### 1.2 Build- und Testpflicht
Build-Kommando vom Projekt-Root, identisch für alle APs:

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

- Nach jeder substanziellen Änderung: Build ausführen.
- Vor Abschluss eines AP: Build muss **fehlerfrei** sein, alle Tests grün.
- Schlägt der Build fehl: Ursache sauber beheben, nicht kaschieren.
- Bestehende Tests dürfen nicht stillschweigend gelöscht oder deaktiviert werden. Sie werden bei Bedarf **angepasst** und der Grund wird im AP-Output dokumentiert.

### 1.3 Pflicht-Tests pro AP
- Jede neue Klasse mit fachlich oder technisch relevanter Logik bekommt mindestens einen Unit-Test.
- Jede in einem AP geänderte Klasse, die bisher Tests hatte, behält Tests; betroffene Tests werden angepasst.
- Pro AP gibt es eine Liste **kritischer Pflicht-Testfälle** (siehe jeweiliges AP). Diese sind namentlich umzusetzen.
- Darüber hinaus gilt die übliche Repo-Praxis (Coverage, PIT-Mutationstests in den unmittelbar betroffenen Modulen, soweit bereits etabliert).

### 1.4 Dokumentation
Pro AP werden mitgepflegt, soweit relevant:
- JavaDoc und `package-info` der berührten Klassen
- Konfigurationsbeispiele
- unmittelbar betroffene Repository-Dokumente

### 1.5 Naming-Regel
In Code, Kommentaren und JavaDoc dürfen **keine** Versions- oder AP-Bezeichner erscheinen:
- Verboten: `V1.0`, `V1.1`, `M1`–`M8`, `AP-001` … `AP-006`
- Stattdessen: zeitlose technische Bezeichnungen.

### 1.6 Pflicht-Output-Format am Ende jedes AP
Am Ende der AP-Bearbeitung gibt Sonnet **genau** diesen Block aus:

```
- Scope erfüllt: ja/nein
- Geänderte Dateien:
  - <Pfad>
  - ...
- Neue Dateien:
  - <Pfad>
  - ...
- Build-Kommando: <verwendetes Kommando>
- Build-Status: ERFOLGREICH / FEHLGESCHLAGEN
- Pflicht-Tests umgesetzt: <Liste der namentlich geforderten Testfälle>
- Offene Punkte: keine / <Beschreibung>
- Risiken: keine / <Beschreibung>
```

---

## 2. Erweiterungsziel und Nicht-Ziele

### 2.1 Ziel
- 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.
- Kein automatischer Fallback, keine Parallelnutzung, keine Profilverwaltung.
- Der fachliche KI-Vertrag (`NamingProposal`) bleibt unverändert.
- Bestehende Properties-Dateien werden beim ersten Start kontrolliert ins neue Schema migriert; vorher wird automatisch eine `.bak`-Sicherung angelegt.

### 2.2 Explizit nicht Bestandteil
- Provider-Familien jenseits der zwei explizit unterstützten
- Profilverwaltung mit mehreren Konfigurationen je Provider-Familie
- automatische Fallback-Umschaltung
- parallele Nutzung mehrerer Provider in einem Lauf
- Änderung des fachlichen Ergebnisvertrags
- Änderung der Dateinamensregeln, Retry-Regeln, Batch-Betriebsmodells
- Persistenz- oder Schemaänderungen jenseits der einen additiven Provider-Identifikator-Spalte

### 2.3 Architekturtreue (unverhandelbar)
- strikte hexagonale Architektur, Abhängigkeiten zeigen nach innen
- `AiNamingPort` bleibt provider-neutral
- provider-spezifische Endpunkte, Header, Auth, Request-/Response-Formate leben **ausschließlich** im jeweiligen Adapter-Out
- keine direkte Adapter-zu-Adapter-Kopplung, keine gemeinsame „abstrakte KI-Adapter"-Zwischenschicht
- die Provider-Auswahl ist eine **Bootstrap-Verdrahtungsentscheidung**

---

## 3. Zielzustand der Konfiguration (verbindlich)

### 3.1 Properties-Schema
```properties
# bestehende, unveränderte Parameter
source.folder=...
target.folder=...
sqlite.file=...
max.retries.transient=...
max.pages=...
max.text.characters=...
prompt.template.file=...
runtime.lock.file=...
log.directory=...
log.level=...
log.ai.sensitive=...

# neue Provider-Auswahl (Pflicht)
ai.provider.active=openai-compatible

# OpenAI-kompatible Provider-Familie
ai.provider.openai-compatible.baseUrl=...
ai.provider.openai-compatible.model=...
ai.provider.openai-compatible.timeoutSeconds=...
ai.provider.openai-compatible.apiKey=...

# Anthropic-Provider-Familie (Claude)
ai.provider.claude.baseUrl=https://api.anthropic.com
ai.provider.claude.model=...
ai.provider.claude.timeoutSeconds=...
ai.provider.claude.apiKey=...
```

### 3.2 Zulässige Werte für `ai.provider.active`
- `openai-compatible`
- `claude`

Jeder andere Wert ist eine ungültige Startkonfiguration und führt zu Exit-Code `1`.

### 3.3 Pflichtwerte je aktivem Provider
| Provider | Pflicht | Optional / mit Default |
|---|---|---|
| `openai-compatible` | `baseUrl`, `model`, `timeoutSeconds`, `apiKey` (Env hat Vorrang) | – |
| `claude` | `model`, `timeoutSeconds`, `apiKey` (Env hat Vorrang) | `baseUrl` (Default `https://api.anthropic.com`) |

Für den **inaktiven** Provider werden keine Pflichtwerte erzwungen.

### 3.4 Umgebungsvariablen für API-Schlüssel
| Provider | Umgebungsvariable |
|---|---|
| `openai-compatible` | `OPENAI_COMPATIBLE_API_KEY` |
| `claude` | `ANTHROPIC_API_KEY` |

- Pro Provider gilt: Umgebungsvariable hat **Vorrang** vor dem Properties-Wert derselben Provider-Familie.
- Schlüssel verschiedener Provider werden **niemals** vermischt.
- Wenn der Betrieb bisher eine andere Umgebungsvariable für den OpenAI-kompatiblen Key genutzt hat, ist diese vom Betreiber auf `OPENAI_COMPATIBLE_API_KEY` umzustellen. Das ist im Abschlussnachweis (AP-006) zu dokumentieren.

### 3.5 Legacy-Form (vor V1.1)
Eindeutig erkennbar an mindestens einem der flachen Schlüssel:
```
api.baseUrl
api.model
api.timeoutSeconds
api.key
```
ohne Vorhandensein von `ai.provider.active`.

---

## 4. Anthropic Messages API – verbindlicher technischer Faktenblock

> Quelle: offizielle Claude API-Dokumentation. Diese Werte sind verbindlich und nicht zu erfinden, abzuleiten oder zu „verbessern".

### 4.1 Endpoint und Methode
- Methode: `POST`
- URL: `{baseUrl}/v1/messages`
- Default-`baseUrl`: `https://api.anthropic.com`

### 4.2 Pflicht-Header
| Header | Wert |
|---|---|
| `x-api-key` | API-Schlüssel aus `ANTHROPIC_API_KEY` (Env) bzw. `ai.provider.claude.apiKey` (Properties) |
| `anthropic-version` | `2023-06-01` |
| `content-type` | `application/json` |

Nicht `Authorization: Bearer …` verwenden. Anthropic nutzt `x-api-key`.

### 4.3 Request-Body (relevante Felder)
```json
{
  "model": "<modellname aus ai.provider.claude.model>",
  "max_tokens": <Integer, > 0, Pflicht>,
  "system": "<optional, top-level Feld - NICHT als Message mit role=system>",
  "messages": [
    { "role": "user", "content": "<Prompt-Text>" }
  ]
}
```
- `max_tokens` ist **Pflicht** (Unterschied zu OpenAI). Konkreter Wert: zweckmäßig fest verdrahtet im Adapter, ausreichend groß für die JSON-Antwort der Anwendung. Kein neuer Properties-Schlüssel.
- `system` wird **nicht** als Message mit `role=system` modelliert. Anthropic akzeptiert nur `user` und `assistant` im `messages`-Array; ein System-Prompt geht ausschließlich ins Top-Level-Feld `system`.
- Der bestehende Prompt der Anwendung wird **unverändert** als Inhalt der einen `user`-Message übergeben. Falls der bestehende Prompt-Mechanismus eine System-Komponente kennt, wandert diese in das `system`-Feld; sonst bleibt `system` weg.

### 4.4 Response-Body (relevante Felder)
```json
{
  "id": "...",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "text", "text": "<die eigentliche Antwort>" }
  ],
  "stop_reason": "...",
  "usage": { "input_tokens": 0, "output_tokens": 0 }
}
```
- Der für die Anwendung relevante Text wird **konkateniert aus allen Blöcken in `content` mit `type == "text"`** in Reihenfolge gewonnen.
- Andere Block-Typen werden ignoriert.
- Liefert die API kein einziges `text`-Block, ist das ein technischer Fehler des Adapters (klassifiziert wie ein leerer/unbrauchbarer Antwortinhalt).

### 4.5 Fehlerklassifikation im Claude-Adapter
| Symptom | Klassifikation | Anmerkung |
|---|---|---|
| HTTP 4xx (außer 429) | technischer Fehler | Auth-Fehler (401/403) zählen hier rein |
| HTTP 429 | technischer Fehler | rate limit |
| HTTP 5xx | technischer Fehler | |
| Timeout | technischer Fehler | |
| Verbindung fehlgeschlagen | technischer Fehler | |
| JSON nicht parsebar | technischer Fehler | |
| Kein `content[*].text`-Block | technischer Fehler | |
| Antworttext nicht nach `NamingProposal` parsebar | greift bestehende Antwort-Validierung der Application | nicht im Adapter behandeln |

Alle technischen Adapterfehler werden auf die **bestehende** transiente Fehlersemantik der Anwendung abgebildet. Es entsteht **keine** neue Fehlerkategorie.

---

## 5. Verbindliche Regeln für jedes AP

1. **Minimale Erweiterung.** Nichts ändern, was nicht für die Erweiterung zwingend erforderlich ist.
2. **Einheitlicher fachlicher KI-Vertrag.** `NamingProposal` bleibt unverändert. Keine provider-spezifische Verzweigung in Application/Domain.
3. **Genau ein aktiver Provider.** Kein Fallback, keine Profilverwaltung.
4. **Properties-Datei bleibt führend.** Keine alternative Konfigurationsquelle.
5. **Bestehender OpenAI-Pfad bleibt funktional unverändert.**
6. **Architekturgrenzen** (siehe 2.3) werden niemals durchbrochen.
7. **Rückwärtsverträglichkeit der SQLite-Daten** bleibt erhalten.
8. **Build muss am Ende jedes AP fehlerfrei sein.**
9. **Alle Pflicht-Testfälle des AP** sind umgesetzt.

---

## 6. Granularität und Reihenfolge

Sechs Arbeitspakete in dieser zwingenden Reihenfolge:

| AP | Thema | Risiko | Charakter |
|---|---|---|---|
| AP-001 | Konfigurations-Schema einführen (additiv) | niedrig | reine Erweiterung |
| AP-002 | Legacy-Migration mit `.bak` | mittel | Datei-Umschreibung, geschützt durch Sicherung |
| AP-003 | Bootstrap-Provider-Auswahl + bestehender Adapter umschalten | hoch | Verhaltensänderung im Wiring |
| AP-004 | Persistenz: Provider-Identifikator additiv | mittel | additive DB-Schema-Migration |
| AP-005 | Nativer Anthropic-Adapter implementieren und verdrahten | mittel | neue Adapter-Klasse |
| AP-006 | Regression, Smoke, Doku, Abschlussnachweis | niedrig | Absicherung |

---

# 7. Arbeitspakete

---

## AP-001 – Konfigurations-Schema einführen (additiv)

### Voraussetzung
Keine.

### Ziel
Das neue, verschachtelte Properties-Schema (Abschnitt 3.1) wird im Code als parsbare und validierbare Struktur eingeführt. Der bestehende Lese- und Validierungspfad bleibt **unangetastet** – das neue Schema wird parallel additiv eingeführt. Es findet **kein** Wechsel im Bootstrap und **keine** Migration in diesem AP.

### Konkret zu erledigende Schritte
1. Im Modul `pdf-umbenenner-application` (oder dem Modul, in dem die heutigen Configuration-Klassen leben – per Typsuche lokalisieren) **neue** Konfigurationstypen einführen, mindestens:
   - eine Repräsentation einer einzelnen Provider-Konfiguration (Felder: `model`, `timeoutSeconds`, `baseUrl`, `apiKey`)
   - eine Repräsentation der Provider-Auswahl (`activeProviderId`) plus Map oder zwei Felder für die beiden Provider-Familien
   - einen klar benannten Aufzählungstyp oder konstanten String-Set für die zulässigen Werte `openai-compatible` und `claude`
2. Im Adapter-Out-Modul den **Properties-Parser** so erweitern, dass er die neuen Schlüssel aus Abschnitt 3.1 erkennt und in die neuen Typen aus Schritt 1 einliest. Der bestehende Parser für die alten flachen Schlüssel bleibt **unverändert** lauffähig (parallele Erkennung).
3. Eine **Validierung** für die neuen Typen einführen. Sie prüft:
   - `ai.provider.active` ist gesetzt und ein zulässiger Wert
   - alle Pflichtwerte des aktiven Providers sind vorhanden (Tabelle 3.3)
   - `timeoutSeconds` ist eine positive Ganzzahl
   - für Claude: Default-`baseUrl` wird gesetzt, wenn der Wert fehlt
   - für den **inaktiven** Provider werden keine Pflichtwerte erzwungen
   - **API-Schlüssel-Auflösung:** Umgebungsvariable des aktiven Providers (Tabelle 3.4) hat Vorrang vor dem Properties-Wert; ist beides leer, ist die Konfiguration ungültig
4. **Bootstrap und bestehende Adapter werden in diesem AP nicht umgestellt.** Die neuen Typen sind ausschließlich über neue Tests erreichbar. Der Default-Lauf der Anwendung verwendet weiterhin die alten Klassen.
5. JavaDoc für alle neuen Klassen und Methoden ergänzen.
6. Konfigurationsbeispiel (`*.example.properties` o.ä.) **nicht** in diesem AP ändern. Folgt in AP-002 zusammen mit der Migration.

### Pflicht-Testfälle (kritisch, namentlich umzusetzen)
1. `parsesNewSchemaWithOpenAiCompatibleActive` – vollständiges neues Schema, OpenAI aktiv, alle Pflichtwerte gesetzt → erfolgreich geparst, Validierung grün.
2. `parsesNewSchemaWithClaudeActive` – vollständiges neues Schema, Claude aktiv, alle Pflichtwerte gesetzt → erfolgreich geparst, Validierung grün.
3. `claudeBaseUrlDefaultsWhenMissing` – Claude aktiv, `ai.provider.claude.baseUrl` fehlt → Default `https://api.anthropic.com` wird gesetzt, Validierung grün.
4. `rejectsMissingActiveProvider` – `ai.provider.active` fehlt → Validierung schlägt fehl mit klarer Meldung.
5. `rejectsUnknownActiveProvider` – `ai.provider.active=foo` → Validierung schlägt fehl.
6. `rejectsMissingMandatoryFieldForActiveProvider` – aktiver Provider hat ein Pflichtfeld leer → Validierung schlägt fehl.
7. `acceptsMissingMandatoryFieldForInactiveProvider` – inaktiver Provider unvollständig → Validierung grün.
8. `envVarOverridesPropertiesApiKeyForActiveProvider` – `OPENAI_COMPATIBLE_API_KEY` gesetzt, Properties-Key ebenfalls gesetzt → effektiver Key ist der aus der Env-Var. Analog für `ANTHROPIC_API_KEY`.
9. `envVarOnlyResolvesForActiveProvider` – Env-Var nur für inaktiven Provider gesetzt, aktiver Provider hat Properties-Key → effektiver Key ist der Properties-Key des aktiven Providers; die Env-Var des inaktiven Providers wird ignoriert.
10. `bestehende Tests bleiben grün` – alle bisherigen Configuration-Tests laufen weiter.

Test-Kategorien zusätzlich: Unit-Tests für die neuen Typen (Equality, Defaults), Parser-Tests, Validator-Tests.

### Explizit NICHT Teil dieses AP
- Migration der Legacy-Datei
- `.bak`-Sicherung
- Bootstrap-Umstellung
- Änderung am bestehenden OpenAI-Adapter
- nativer Claude-Adapter
- Persistenz-Änderungen
- Logging-Änderungen

### Definition of Done
- Build fehlerfrei
- alle Pflicht-Testfälle umgesetzt und grün
- bestehende Tests grün
- JavaDoc vollständig für neue Klassen
- Pflicht-Output-Block ausgegeben

---

## AP-002 – Legacy-Migration mit `.bak`

### Voraussetzung
AP-001 abgeschlossen.

### Ziel
Beim ersten Start mit erkannter Legacy-Form wird die Properties-Datei kontrolliert in das neue Schema überführt. Vor jeder Migration wird eine `.bak`-Sicherung angelegt. Nach erfolgreicher Migration läuft die Anwendung **noch** auf dem alten Bootstrap-Pfad weiter (Umschaltung folgt in AP-003); aber die Datei auf der Platte ist bereits im neuen Format und beim nächsten Start sofort durch das neue Schema lesbar.

### Konkret zu erledigende Schritte
1. Eine neue Komponente im Adapter-Out-Modul anlegen, die rein auf Properties-Datei-Ebene arbeitet (kein HTTP, kein DB-Zugriff). Verantwortlichkeiten:
   - Erkennen der Legacy-Form (Abschnitt 3.5)
   - `.bak`-Sicherung anlegen: `<dateiname>.bak`. Wenn `.bak` schon existiert, mit aufsteigendem numerischen Suffix sichern (`<dateiname>.bak`, `<dateiname>.bak.1`, …) – **niemals** überschreiben.
   - Werte umschreiben gemäß Tabelle:
     | Legacy | Ziel |
     |---|---|
     | `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` ergänzen.
   - Leere/auskommentierte Platzhalter für die Claude-Sektion einfügen mit kurzem Hinweis-Kommentar (ein Block, max. 6 Zeilen).
   - Alle übrigen Schlüssel (`source.folder`, `target.folder`, `sqlite.file`, `max.*`, `prompt.template.file`, `runtime.lock.file`, `log.*`) **unverändert** und in **stabiler Reihenfolge** übernehmen.
   - Die migrierte Datei in-place schreiben (`.tmp` + atomischer Move/Rename, kein Truncate-and-write auf das Original).
   - Anschließend die Datei erneut über den **neuen** Parser aus AP-001 laden und über den neuen Validator validieren. Schlägt das fehl, ist dies ein harter Startfehler (Exit-Code 1, klare Meldung, `.bak` bleibt erhalten).
2. Die Migration wird beim Programmstart **vor** dem bestehenden Konfigurationsladen aufgerufen, sobald die Datei bekannt ist. Dieser Aufruf passiert im Bootstrap genau an einer Stelle und ist als eigene Methode klar benennbar.
3. Wird **kein** Legacy erkannt (also bereits neues Schema), passiert nichts: keine `.bak`, keine Schreibvorgänge.
4. Bestehende ConfigurationPort-Implementierung **nicht** umstellen – das passiert in AP-003. Die Anwendung läuft nach AP-002 fachlich weiter wie zuvor; ihr Eingangs-File ist nur jetzt in beiden Formen lesbar.
5. Konfigurationsbeispiel im Repo (z. B. `*.example.properties`) auf das **neue** Schema umstellen. Die Datei zeigt beide Provider-Sektionen mit sprechenden Platzhalterwerten.
6. JavaDoc und kurzer Abschnitt in der Repo-Doku zur Migration ergänzen (was passiert, wann, wie wird gesichert, was bei Fehler).

### Pflicht-Testfälle
1. `migratesLegacyFileWithAllFlatKeys` – Legacy-Datei mit allen vier `api.*`-Schlüsseln wird korrekt ins neue Schema überführt; Werte bleiben inhaltlich identisch; übrige Schlüssel bleiben unverändert.
2. `createsBakBeforeOverwriting` – vor Migration existiert keine `.bak`, danach existiert sie mit dem **Original-Inhalt**.
3. `bakSuffixIsIncrementedIfBakExists` – `.bak` existiert bereits → neue Sicherung als `.bak.1`. Keine Sicherung wird überschrieben.
4. `noOpForAlreadyMigratedFile` – Datei bereits im neuen Schema → kein Schreibvorgang, kein `.bak`.
5. `reloadAfterMigrationSucceeds` – nach Migration kann der neue Parser/Validator aus AP-001 die Datei fehlerfrei laden.
6. `migrationFailureKeepsBak` – Migration schreibt fehlerhafte Datei (Test-Mock erzwingt Validierungsfehler nach Schreiben) → Bootstrap meldet harten Startfehler, `.bak` ist unangetastet.
7. `legacyDetectionRequiresAtLeastOneFlatKey` – Datei mit `ai.provider.active=...` und ohne `api.*` → kein Legacy, keine Migration.
8. `legacyValuesEndUpInOpenAiCompatibleNamespace` – Werte `api.baseUrl`, `api.model`, `api.timeoutSeconds`, `api.key` landen exakt in den vier Zielschlüsseln; `ai.provider.active=openai-compatible` ist gesetzt.
9. `unrelatedKeysSurviveUnchanged` – Schlüssel wie `source.folder`, `max.pages`, `log.level` bleiben mit identischem Wert erhalten.
10. `inPlaceWriteIsAtomic` – Test-Doppel für das Dateisystem belegt: erst `.tmp` schreiben, dann atomic move; kein Punkt, an dem das Original teilbeschrieben ist.

Test-Kategorien zusätzlich: temporäre Dateien in `@TempDir`, Repository-/Integrationstests für die Migrations-Komponente.

### Explizit NICHT Teil
- Bootstrap-Umstellung des aktiven Konfigurationspfads
- Änderung am bestehenden OpenAI-Adapter
- Claude-Adapter
- Persistenz
- Logging-Änderungen über die Migrations-Meldungen hinaus

### Definition of Done
- Build fehlerfrei, alle Pflicht-Testfälle grün
- Beispiel-Properties-Datei im neuen Schema
- Kurz-Doku zur Migration im Repo
- Pflicht-Output-Block ausgegeben

---

## AP-003 – Bootstrap-Provider-Auswahl und Umstellung des bestehenden OpenAI-Adapters

### Voraussetzung
AP-001 und AP-002 abgeschlossen.

### Ziel
Das Bootstrap-Modul wählt anhand von `ai.provider.active` genau eine `AiNamingPort`-Implementierung als aktive Implementierung aus und verdrahtet sie. Der bestehende OpenAI-kompatible Adapter konsumiert ab jetzt seine Werte aus dem Namensraum `ai.provider.openai-compatible.*`. Sein fachliches Verhalten bleibt **unverändert**. Der aktive Provider wird beim Laufstart geloggt.

### Konkret zu erledigende Schritte
1. Im Bootstrap-Modul eine **Provider-Selektor-Komponente** einführen, die als Eingabe den Wert von `ai.provider.active` und alle bekannten `AiNamingPort`-Implementierungen erhält und genau eine zurückgibt. Initial kennt sie nur die OpenAI-Implementierung; die Erweiterung um Claude erfolgt in AP-005 an genau dieser Stelle.
2. Bestehende `AiNamingPort`-Implementierung für die OpenAI-kompatible Schnittstelle so anpassen, dass sie die Werte aus `ai.provider.openai-compatible.*` konsumiert. Der bisherige fachliche Vertrag, das Request-/Response-Mapping und das Fehlerverhalten bleiben **identisch**.
3. Bestehenden ConfigurationPort/`Configuration`-Lesepfad so umstellen, dass intern **nur noch** das neue Schema verwendet wird. Die alten flachen Klassen/Methoden, die nur zum Lesen von `api.*` dienten, werden entfernt – aber **nur**, wenn sie nirgends sonst benötigt werden (per Suche prüfen). Falls noch Verweise existieren, wird der entsprechende Konsument im selben AP auf das neue Schema umgestellt.
4. Bestehende Konfigurations-Tests des Repos auf das neue Schema umstellen. Tests, die explizit das alte flache Schema geprüft haben, werden zu Migrations-Tests verschoben (gehört bereits zu AP-002) **oder** auf das neue Schema umgeschrieben. Kein Test wird stillschweigend gelöscht.
5. Logging-Anbindung erweitern: beim Laufstart wird der **aktive Provider-Identifikator** geloggt (Standard-Loglevel `INFO`). Alle übrigen geforderten Log-Inhalte (siehe `CLAUDE.md`, Logging-Mindestumfang) bleiben unverändert.
6. Sicherstellen, dass die Sensibilitätsregel für KI-Inhalte unverändert greift und provider-unabhängig gilt.
7. Adapter-zu-Adapter-Kopplung aktiv vermeiden: Der Provider-Selektor lebt im Bootstrap, **nicht** im Adapter-Out-Modul.
8. JavaDoc für Selektor und betroffene Klassen ergänzen.

### Pflicht-Testfälle
1. `bootstrapWiresOpenAiCompatibleAdapterWhenActive` – `ai.provider.active=openai-compatible` → Selektor liefert die OpenAI-Implementierung.
2. `bootstrapFailsHardWhenActiveProviderUnknown` – Wert ist syntaktisch gesetzt, aber kein gültiger Provider → harter Startfehler, Exit-Code 1.
3. `bootstrapFailsHardWhenSelectedProviderHasNoImplementation` – Wert ist `claude`, aber Implementierung noch nicht registriert (Zustand nach AP-003) → harter Startfehler mit klarer Meldung. Dieser Test wird in AP-005 angepasst, sobald Claude registriert ist.
4. `openAiAdapterReadsValuesFromNewNamespace` – Adapter-Test: gegebene `ai.provider.openai-compatible.*`-Werte landen 1:1 im HTTP-Request an die bisherige Endpoint-URL.
5. `openAiAdapterBehaviorIsUnchanged` – bestehender Adapter-Verhaltenstest (Request-Form, Response-Mapping, Fehlerklassifikation) wird auf die neue Konfigurationsquelle umgestellt und bleibt grün.
6. `activeProviderIsLoggedAtRunStart` – Smoke- oder Bootstrap-Test belegt, dass der aktive Provider bei Laufstart in einem definierten Log-Eintrag erscheint.
7. `existingDocumentProcessingTestsRemainGreen` – sämtliche bestehenden End-to-End-/Integrations-Tests des bestehenden OpenAI-Pfads bleiben grün, ggf. mit angepasster Konfiguration.
8. `legacyFileEndToEndStillRuns` – Test-Doppel: Anwendung startet mit Legacy-Datei → Migration aus AP-002 läuft → Bootstrap aus AP-003 wählt OpenAI → Lauf läuft fachlich durch wie zuvor.

Test-Kategorien zusätzlich: Bootstrap-/Wiring-Tests, ggf. Smoke-Test ohne realen externen Aufruf.

### Explizit NICHT Teil
- Claude-Adapter
- Persistenz-Erweiterung um Provider-Identifikator
- neue Fehlersemantik
- Refactoring außerhalb der Adapter-Anbindung

### Definition of Done
- Build fehlerfrei, Pflicht-Testfälle grün
- bestehender OpenAI-Pfad fachlich unverändert
- aktiver Provider wird beim Laufstart geloggt
- keine Verweise mehr auf das alte flache Schema im Produktivpfad
- Pflicht-Output-Block ausgegeben

---

## AP-004 – Persistenz: Provider-Identifikator additiv

### Voraussetzung
AP-003 abgeschlossen.

### Ziel
Das SQLite-Schema wird **additiv** um eine Spalte für den Provider-Identifikator je Versuch erweitert. Bestehende Datensätze bleiben lesbar und korrekt interpretierbar (Default-Wert für Altdaten). Neue Versuche schreiben den Identifikator des für den Versuch aktiven Providers.

### Konkret zu erledigende Schritte
1. Im SQLite-Schema der Versuchshistorie eine neue Spalte hinzufügen, z. B. `ai_provider TEXT NULL` (Spaltenname per bestehender Repo-Konvention wählen, sonst wie hier vorgeschlagen). Die Spalte ist nullable.
2. Schema-Migration umsetzen:
   - Bei Programmstart prüfen, ob die Spalte existiert; wenn nein, per `ALTER TABLE` ergänzen.
   - Vorhandene Zeilen behalten den Wert `NULL`.
   - Migration muss idempotent sein (mehrfacher Start ohne Fehler).
3. Die Versuchshistorie-Schreiblogik so erweitern, dass beim Anlegen eines neuen Versuchs der **Identifikator des aktiv ausgewählten Providers** mitgeschrieben wird (`openai-compatible` oder `claude`). Der Wert kommt aus der bereits in AP-003 verfügbaren Provider-Auswahl.
4. Dokument-Stammsatz wird **nicht** verändert.
5. Lesepfad anpassen, sodass der neue Wert mitausgelesen wird; bestehende Mapper/Domain-Typen werden minimal um ein optionales Feld erweitert. Application und Domain bekommen dadurch keinen provider-spezifischen Code – das Feld bleibt ein opaker String.
6. JavaDoc und kurzer Abschnitt zur Schema-Erweiterung in der Repo-Doku ergänzen.

### Pflicht-Testfälle
1. `addsProviderColumnOnFreshDb` – frische DB → Schema enthält neue Spalte.
2. `addsProviderColumnOnExistingDbWithoutColumn` – DB ohne Spalte (Simulation Altbestand) → Migration legt Spalte nullable an.
3. `migrationIsIdempotent` – mehrfacher Start ändert nichts und wirft keinen Fehler.
4. `existingRowsKeepNullProvider` – Altzeilen behalten `NULL`.
5. `newAttemptsWriteOpenAiCompatibleProvider` – aktiver Provider OpenAI → neuer Versuch hat `ai_provider='openai-compatible'`.
6. `newAttemptsWriteClaudeProvider` – aktiver Provider Claude (für diesen Test wird die Provider-Auswahl gemockt; in AP-005 wird derselbe Test mit echtem Claude-Adapter wiederholt) → `ai_provider='claude'`.
7. `repositoryReadsProviderColumn` – Repository-Test: gespeicherter Wert wird korrekt zurückgelesen.
8. `legacyDataReadingDoesNotFail` – Test mit DB-Datei aus dem Vor-V1.1-Stand: Lesen ohne Fehler, neuer Wert ist Optional/leer.
9. `existingHistoryTestsRemainGreen` – alle bestehenden Tests rund um die Versuchshistorie bleiben grün, ggf. mit minimaler Anpassung.

Test-Kategorien zusätzlich: Repository-Tests gegen echte SQLite-Instanz (in-memory oder temporär), Schema-Migrations-Tests.

### Explizit NICHT Teil
- Claude-Adapter (folgt in AP-005)
- Änderungen am Dokument-Stammsatz
- neue Wahrheitsquellen
- Reporting/Statistiken

### Definition of Done
- Build fehlerfrei, Pflicht-Testfälle grün
- bestehende Datenbestände bleiben lesbar
- Provider-Identifikator wird für neue Versuche geschrieben
- Pflicht-Output-Block ausgegeben

---

## AP-005 – Nativer Anthropic-Adapter implementieren und verdrahten

### Voraussetzung
AP-001 bis AP-004 abgeschlossen.

### Ziel
Eine zweite `AiNamingPort`-Implementierung wird im Adapter-Out-Modul angelegt, die die **native Anthropic Messages API** anspricht (siehe Faktenblock in Abschnitt 4). Sie wird im Provider-Selektor aus AP-003 als zweite Option registriert. Der Adapter bildet die Anthropic-Antwort auf den **bestehenden** fachlichen Vertrag ab; es entsteht kein Sonderweg in Application oder Domain.

### Konkret zu erledigende Schritte
1. Im Adapter-Out-Modul eine neue Klasse anlegen, die `AiNamingPort` implementiert. Naming nach bestehender Repo-Konvention; per Typsuche prüfen, wie die OpenAI-Implementierung benannt ist, und analog vorgehen.
2. HTTP-Aufruf gemäß Faktenblock 4 umsetzen:
   - URL aus `ai.provider.claude.baseUrl` (Default `https://api.anthropic.com`) plus Pfad `/v1/messages`
   - Methode `POST`
   - Header `x-api-key`, `anthropic-version: 2023-06-01`, `content-type: application/json`
   - Request-Body mit `model`, `max_tokens`, `messages` (eine `user`-Message mit dem bestehenden Prompt-Text), optional `system` falls die bestehende Prompt-Mechanik ein System-Segment kennt
   - Timeout aus `ai.provider.claude.timeoutSeconds`
3. API-Schlüssel-Auflösung exakt nach Tabelle 3.4: zuerst `ANTHROPIC_API_KEY`, dann `ai.provider.claude.apiKey`.
4. Antwortverarbeitung gemäß 4.4: Konkatenation aller `content[*].text`-Blöcke in Reihenfolge. Fehlt jeder `text`-Block oder ist die Antwort nicht parsebar → technischer Adapterfehler nach Tabelle 4.5.
5. Den so gewonnenen Antworttext **unverändert** an die bestehende Antwortverarbeitung der Anwendung weitergeben (`NamingProposal`-Validierung passiert in Application/Domain wie bisher).
6. Fehlerklassifikation streng nach Tabelle 4.5. Keine neuen Fehlerklassen.
7. Den Provider-Selektor aus AP-003 um die neue Implementierung erweitern. **Keine** gemeinsame Basisklasse zwischen den beiden Adaptern, **keine** Hilfsklasse, die HTTP-Logik teilt. Was beide Adapter brauchen, kommt aus dem Repo-üblichen HTTP-/JSON-Standard, nicht aus einer neuen Adapter-Zwischenschicht.
8. Den in AP-001 angelegten Test `bootstrapFailsHardWhenSelectedProviderHasNoImplementation` so anpassen, dass er ab jetzt auf einen neuen, weiterhin **unbekannten** Provider-Wert testet (Negativfall bleibt erhalten, aber `claude` ist jetzt registriert).
9. Konfigurationsbeispiel im Repo um sprechende Claude-Beispielwerte ergänzen.
10. JavaDoc für die neue Klasse und ggf. neue Hilfstypen.

### Pflicht-Testfälle
1. `claudeAdapterBuildsCorrectRequest` – gegebener Prompt → HTTP-Request mit korrekter URL (`<baseUrl>/v1/messages`), Methode POST, allen drei Pflicht-Headern, Body enthält `model`, `max_tokens > 0`, `messages` mit genau einer `user`-Message und korrektem Prompt.
2. `claudeAdapterUsesEnvVarApiKey` – `ANTHROPIC_API_KEY` gesetzt, Properties-Wert ebenfalls → Header `x-api-key` enthält den Env-Wert.
3. `claudeAdapterFallsBackToPropertiesApiKey` – Env-Var leer, Properties-Wert gesetzt → Header `x-api-key` enthält den Properties-Wert.
4. `claudeAdapterFailsValidationWhenBothKeysMissing` – beides leer → Konfigurationsfehler beim Start (greift auf AP-001-Validierung).
5. `claudeAdapterParsesSingleTextBlock` – Mock-Response mit einem Block `{type:"text", text:"..."}` → Antworttext gleich dem Block-Text.
6. `claudeAdapterConcatenatesMultipleTextBlocks` – mehrere `text`-Blöcke → Antworttext gleich der Konkatenation in Reihenfolge.
7. `claudeAdapterIgnoresNonTextBlocks` – Mix aus `text`- und Nicht-`text`-Blöcken → nur die `text`-Inhalte landen im Antworttext.
8. `claudeAdapterFailsOnEmptyTextContent` – Response ohne jeden `text`-Block → technischer Adapterfehler.
9. `claudeAdapterMapsHttp401AsTechnical` – Mock-Response 401 → technischer Fehler nach Tabelle 4.5.
10. `claudeAdapterMapsHttp429AsTechnical` – Mock-Response 429 → technischer Fehler.
11. `claudeAdapterMapsHttp500AsTechnical` – Mock-Response 500 → technischer Fehler.
12. `claudeAdapterMapsTimeoutAsTechnical` – simulierter Timeout → technischer Fehler.
13. `claudeAdapterMapsUnparseableJsonAsTechnical` – Response-Body ist kein gültiges JSON → technischer Fehler.
14. `bootstrapSelectsClaudeWhenActive` – `ai.provider.active=claude` → Selektor liefert die Claude-Implementierung.
15. `claudeProviderIdentifierLandsInAttemptHistory` – End-zu-End mit gemocktem HTTP-Layer: nach erfolgreichem Lauf hat der neue Versuch `ai_provider='claude'` (knüpft an AP-004 an).
16. `existingOpenAiPathRemainsGreen` – sämtliche bestehenden Tests des OpenAI-Pfads bleiben unverändert grün.

Test-Kategorien zusätzlich: Adapter-Tests mit gemocktem HTTP-Client (kein realer Netzwerkzugriff), Bootstrap-Wiring-Tests.

### Explizit NICHT Teil
- automatische Fallback-Logik zwischen Providern
- gemeinsame Adapter-Basisklasse
- Erweiterung des Persistenz-Schemas über AP-004 hinaus
- Anpassung des Prompts (eine etwaige System-/User-Trennung der bestehenden Prompt-Datei darf genutzt werden, aber keine inhaltliche Änderung des Prompts)

### Definition of Done
- Build fehlerfrei, alle Pflicht-Testfälle grün
- nativer Anthropic-Adapter wird über Konfiguration auswählbar und liefert auf Mock-Basis korrekte Ergebnisse
- bestehender OpenAI-Pfad unverändert grün
- Pflicht-Output-Block ausgegeben

---

## AP-006 – Regression, Smoke, Doku-Konsolidierung, Abschlussnachweis

### Voraussetzung
AP-001 bis AP-005 abgeschlossen.

### Ziel
Der vollständige Erweiterungsstand wird automatisiert abgesichert, dokumentarisch konsolidiert und als minimale, architekturtreue Erweiterung des Basisstands belastbar nachgewiesen.

### Konkret zu erledigende Schritte
1. **Smoke-Test je Provider:** Zwei Smoke-Tests einrichten, die für je eine Provider-Konfiguration den Bootstrap-Pfad bis zur erfolgreichen Verdrahtung des `AiNamingPort` durchlaufen, **ohne** realen externen HTTP-Aufruf (gemockter HTTP-Layer). Beide müssen grün sein.
2. **Regression OpenAI:** Alle bestehenden End-to-End-/Integrations-Tests des OpenAI-Pfads laufen grün. Falls Anpassungen in vorigen APs Tests berührt haben, ist hier der finale Konsistenz-Check.
3. **Migration Smoke:** Ein End-zu-End-Test, der mit einer Legacy-Datei (Inhalt aus der bekannten Demo-Konfig) startet und nach einem ersten Lauf folgendes nachweist:
   - `.bak` existiert mit Original-Inhalt
   - Properties-Datei ist im neuen Schema
   - `ai.provider.active=openai-compatible`
   - der Lauf hat fachlich gleich funktioniert wie mit dem neuen Schema
4. **PIT-/Mutationstests** in den unmittelbar betroffenen Modulen ausführen, soweit bereits etabliert. Lücken im neuen Code, die deutlich unter dem bestehenden Niveau liegen, gezielt schließen. Keine willkürliche Coverage-Kosmetik.
5. **Doku-Konsolidierung:**
   - Beispiel-Properties-Datei zeigt das vollständige neue Schema für **beide** Provider mit sprechenden Platzhaltern.
   - Repo-Doku enthält einen kurzen Abschnitt „KI-Provider auswählen" mit den zulässigen Werten und der Env-Var-Konvention (`OPENAI_COMPATIBLE_API_KEY`, `ANTHROPIC_API_KEY`).
   - Repo-Doku enthält einen kurzen Abschnitt „Migration von der Vorgängerversion" mit dem Hinweis auf `.bak`.
   - JavaDoc aller in der Erweiterung neu eingeführten oder substanziell geänderten Klassen ist vorhanden.
6. **Abschlussnachweis:** Eine kurze, im Repository verbleibende Markdown-Datei unter `docs/workpackages/V1.1 - Abschlussnachweis.md` anlegen, die mindestens enthält:
   - Datum, betroffene Module
   - Liste der ausgeführten Pflicht-Testfälle pro AP (kann tabellarisch sein)
   - Belegte Eigenschaften: zwei Provider unterstützt, genau einer aktiv, kein Fallback, fachlicher Vertrag unverändert, Persistenz rückwärtsverträglich, Migration nachgewiesen, `.bak` nachgewiesen, aktiver Provider geloggt
   - explizite Bestätigung: keine Architekturbrüche, keine neuen Bibliotheken außer denen, die für HTTP/JSON ohnehin im Repo etabliert sind
   - Hinweis auf die Betreiberaufgabe, ggf. die Umgebungsvariable des OpenAI-Keys auf `OPENAI_COMPATIBLE_API_KEY` umzustellen
7. Den vollständigen Reactor-Build ausführen und das Ergebnis im AP-Output festhalten.

### Pflicht-Testfälle
1. `smokeBootstrapWithOpenAiCompatibleActive`
2. `smokeBootstrapWithClaudeActive`
3. `e2eMigrationFromLegacyDemoConfig`
4. `regressionExistingOpenAiSuiteGreen` (Sammelnachweis, nicht ein einzelner Test)
5. `e2eClaudeRunWritesProviderIdentifierToHistory`
6. `e2eOpenAiRunWritesProviderIdentifierToHistory`
7. `legacyDataFromBeforeV11RemainsReadable`

Test-Kategorien zusätzlich: Mutationstests in betroffenen Modulen, Konsistenz-Checks der Doku-Beispiele gegen den realen Parser (z. B. „Beispiel-Properties-Datei wird vom Parser ohne Fehler geladen").

### Explizit NICHT Teil
- weitere Provider
- Komfortfunktionen
- großflächiges Refactoring

### Definition of Done
- vollständiger Reactor-Build fehlerfrei
- alle Pflicht-Testfälle grün
- Smoke-Tests je Provider grün
- Doku konsolidiert
- Abschlussnachweis-Datei im Repo
- Pflicht-Output-Block ausgegeben
