diff --git a/docs/workpackages/V1.1 - Arbeitspakete.md b/docs/workpackages/V1.1 - Arbeitspakete.md new file mode 100644 index 0000000..8e93b75 --- /dev/null +++ b/docs/workpackages/V1.1 - Arbeitspakete.md @@ -0,0 +1,596 @@ +# 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: + - + - ... +- Neue Dateien: + - + - ... +- Build-Kommando: +- Build-Status: ERFOLGREICH / FEHLGESCHLAGEN +- Pflicht-Tests umgesetzt: +- Offene Punkte: keine / +- Risiken: keine / +``` + +--- + +## 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": "", + "max_tokens": 0, Pflicht>, + "system": "", + "messages": [ + { "role": "user", "content": "" } + ] +} +``` +- `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": "" } + ], + "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: `.bak`. Wenn `.bak` schon existiert, mit aufsteigendem numerischen Suffix sichern (`.bak`, `.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 (`/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