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

37 KiB
Raw Permalink Blame History

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, M1M8, AP-001AP-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

# 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)

{
  "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)

{
  "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