1
0
Files
pdf-umbenenner/docs/workpackages/V1.1 - Arbeitspakete.md

597 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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