Kleinere Korrekturen
This commit is contained in:
332
docs/specs/V1.1 - Ist-Stand.md
Normal file
332
docs/specs/V1.1 - Ist-Stand.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# V1.1 – Ist-Stand des PDF-Umbenenners
|
||||
|
||||
## Zweck dieses Dokuments
|
||||
|
||||
Dieses Dokument beschreibt den **tatsächlichen Ist-Stand der Software bis einschließlich V1.1**.
|
||||
Es ergänzt die bestehenden Spezifikations- und Projektdokumente um den konkret erreichten Erweiterungsstand nach der vollständig umgesetzten und freigegebenen V1.
|
||||
|
||||
Ziel ist, dass eine KI oder ein Entwickler zusammen mit den übrigen Repository-Dokumenten den aktuellen Funktionsumfang und die Architektur **präzise und ohne Rückgriff auf Chat-Verläufe** verstehen kann.
|
||||
|
||||
Dieses Dokument ist **kein Soll-Konzept** und **kein neues Arbeitspaket**, sondern eine Beschreibung des erreichten Stands.
|
||||
|
||||
---
|
||||
|
||||
## Einordnung des Stands
|
||||
|
||||
### Ausgangsbasis
|
||||
|
||||
Vor V1.1 lag eine vollständig umgesetzte, getestete, dokumentierte und freigegebene **V1** des Projekts vor.
|
||||
Diese V1 entsprach dem Umsetzungsstand der Meilensteine **M1 bis M8**.
|
||||
|
||||
Damit war insbesondere bereits vorhanden:
|
||||
|
||||
- vollständiges Maven-Multi-Module-Projekt
|
||||
- strikte hexagonale Architektur
|
||||
- Batch-Verarbeitung über Standalone-JAR
|
||||
- PDF-Erkennung und PDF-Textauslese
|
||||
- SQLite-Persistenz
|
||||
- Retry-, Skip- und Statuslogik
|
||||
- KI-gestützte Ermittlung eines Benennungsvorschlags
|
||||
- Dateinamensbildung und Zielkopie
|
||||
- Logging, Qualitätsabsicherung, Dokumentation und Freigabestand
|
||||
|
||||
### Ziel von V1.1
|
||||
|
||||
V1.1 wurde bewusst **klein und minimal-invasiv** definiert.
|
||||
|
||||
Es handelt sich **nicht** um einen allgemeinen V2-Ausbau und **nicht** um einen Produktumbau mit GUI, Installer oder EXE.
|
||||
|
||||
Der Fokus von V1.1 ist ausschließlich:
|
||||
|
||||
- Erweiterung der bestehenden KI-Anbindung um **native Claude-Unterstützung**
|
||||
- gleichzeitiger Erhalt des bisherigen **OpenAI-kompatiblen Wegs**
|
||||
- Mehrprovider-Konfiguration mit **genau einem aktiven Provider**
|
||||
- Wahrung der bestehenden Architekturprinzipien
|
||||
- Wahrung der fachlichen und technischen Regeln aus V1
|
||||
- Abwärtskompatibilität bestehender Konfigurationen
|
||||
|
||||
---
|
||||
|
||||
## Inhaltlicher Umfang von V1.1
|
||||
|
||||
V1.1 erweitert die bestehende Anwendung um eine **Mehrprovider-fähige KI-Konfiguration**, bei der genau **ein Provider aktiv** ist.
|
||||
|
||||
### In V1.1 enthalten
|
||||
|
||||
- Unterstützung von **zwei real nutzbaren KI-Providern**
|
||||
- OpenAI-kompatibler Provider
|
||||
- nativer Claude-Provider
|
||||
- Properties-basierte Konfiguration für mehrere Provider
|
||||
- Auswahl genau **eines aktiven Providers**
|
||||
- Beibehaltung des bisherigen fachlichen KI-Vertrags
|
||||
- Integration der Providerwahl in die bestehende technische Konfiguration
|
||||
- Migration alter V1-Konfigurationen auf die neue Struktur
|
||||
- Sicherung alter Konfigurationen über `.bak`
|
||||
- vollständige Tests und Nachschärfung der Build-/Qualitätshygiene
|
||||
|
||||
### In V1.1 ausdrücklich **nicht** enthalten
|
||||
|
||||
- GUI
|
||||
- Installer
|
||||
- EXE-Paketierung
|
||||
- mehrere Profile pro Provider
|
||||
- automatischer Fallback zwischen Providern
|
||||
- neue fachliche Regeln für Dateinamen
|
||||
- neue Retry-Semantik
|
||||
- neue Statussemantik
|
||||
- neue Persistenz-Wahrheiten
|
||||
- Änderungen am Grundprinzip des Batch-Betriebs
|
||||
|
||||
---
|
||||
|
||||
## Fachlich-technische Kernaussage von V1.1
|
||||
|
||||
Die Anwendung verarbeitet weiterhin OCR-verarbeitete PDF-Dateien lokal und erzeugt daraus im Erfolgsfall korrekt benannte Zielkopien.
|
||||
|
||||
Der Unterschied zu V1 ist:
|
||||
|
||||
> Die Ermittlung des KI-basierten Benennungsvorschlags kann nun wahlweise
|
||||
> über einen **OpenAI-kompatiblen Provider** oder über die **native Claude-API**
|
||||
> erfolgen.
|
||||
|
||||
Dabei bleibt für den restlichen fachlichen Ablauf entscheidend:
|
||||
|
||||
- der Input bleibt ein aufbereiteter Dokumenttext
|
||||
- die KI liefert weiterhin denselben fachlichen Vorschlagsinhalt
|
||||
- der restliche Verarbeitungsfluss bleibt unverändert
|
||||
|
||||
---
|
||||
|
||||
## Unveränderte Regeln aus V1
|
||||
|
||||
V1.1 ändert **nicht** die fachlichen Kernregeln des Systems.
|
||||
|
||||
Insbesondere unverändert bleiben:
|
||||
|
||||
- Zielformat: `YYYY-MM-DD - Titel.pdf`
|
||||
- Dublettenregel `(1)`, `(2)`, …
|
||||
- 20-Zeichen-Regel für den Basistitel
|
||||
- deutsche, verständliche Titel
|
||||
- Quelldatei bleibt unverändert
|
||||
- Fingerprint-basierte Identifikation
|
||||
- SQLite als lokaler Persistenzspeicher
|
||||
- Retry- und Skip-Regeln
|
||||
- Statusmodell
|
||||
- Run-Lock
|
||||
- Start über Standalone-JAR / Task Scheduler
|
||||
- keine Dauerlauf-Anwendung
|
||||
- keine GUI / kein Webserver / kein App-Server
|
||||
|
||||
---
|
||||
|
||||
## Architekturstand in V1.1
|
||||
|
||||
V1.1 wahrt die bestehende Architektur.
|
||||
|
||||
### Unverändert gültig
|
||||
|
||||
- strikte hexagonale Architektur
|
||||
- Ports and Adapters
|
||||
- Abhängigkeiten zeigen nach innen
|
||||
- keine Infrastruktur im Domain-Kern
|
||||
- keine direkte Adapter-zu-Adapter-Kopplung
|
||||
- Logging bleibt technische Infrastruktur
|
||||
- Konfiguration bleibt technische Infrastruktur
|
||||
- Bootstrap verdrahtet die konkrete Laufzeitumgebung
|
||||
|
||||
### Bedeutung für die KI-Integration
|
||||
|
||||
Die Mehrprovider-Fähigkeit wird **architekturtreu** eingeführt:
|
||||
|
||||
- der fachliche/application-seitige KI-Vertrag bleibt erhalten
|
||||
- die Provider-spezifischen Unterschiede liegen in den technischen Adaptern
|
||||
- die konkrete Providerauswahl erfolgt über Konfiguration und Bootstrap
|
||||
- es entsteht keine provider-spezifische Fachlogik im Kern
|
||||
|
||||
---
|
||||
|
||||
## KI-Provider in V1.1
|
||||
|
||||
### Unterstützte Provider
|
||||
|
||||
V1.1 unterstützt genau diese zwei Providerarten:
|
||||
|
||||
1. **OpenAI-kompatibel**
|
||||
2. **Claude nativ**
|
||||
|
||||
### Aktiver Provider
|
||||
|
||||
Es gibt immer **genau einen aktiven Provider**.
|
||||
|
||||
V1.1 führt **keinen** automatischen Fallback zwischen Providern ein.
|
||||
Wenn der aktive Provider fehlschlägt, gilt der bestehende Fehler- und Retry-Rahmen.
|
||||
|
||||
### Keine Profilverwaltung
|
||||
|
||||
Pro Provider gibt es in V1.1 genau **eine** Konfiguration.
|
||||
Benannte Profile oder mehrere alternative Konfigurationssätze pro Provider sind nicht Bestandteil von V1.1.
|
||||
|
||||
---
|
||||
|
||||
## KI-Vertrag bleibt unverändert
|
||||
|
||||
V1.1 ändert **nicht** den fachlichen Ergebnisvertrag der KI.
|
||||
|
||||
Die Erweiterung betrifft den technischen Zugriffsweg auf die KI, **nicht** den fachlichen Inhalt der KI-Antwort.
|
||||
|
||||
Damit bleibt der Grundsatz bestehen:
|
||||
|
||||
- die KI liefert denselben fachlichen Vorschlagsinhalt wie zuvor
|
||||
- die Anwendung behält die Hoheit über Validierung, Datumsauflösung, Titelregeln und weitere technische Verarbeitung
|
||||
- der restliche Systemablauf bleibt gegenüber V1 stabil
|
||||
|
||||
---
|
||||
|
||||
## Konfigurationsmodell in V1.1
|
||||
|
||||
### Allgemeiner Grundsatz
|
||||
|
||||
Die `.properties`-Datei bleibt die **Wahrheit** der Konfiguration.
|
||||
|
||||
### Erweiterung in V1.1
|
||||
|
||||
Die Konfiguration kann nun mehrere Provider-Konfigurationen enthalten, von denen genau **eine aktiv** ist.
|
||||
|
||||
### Abwärtskompatibilität
|
||||
|
||||
Bestehende V1-Konfigurationen bleiben nutzbar.
|
||||
|
||||
Dazu wurde in V1.1 vorgesehen:
|
||||
|
||||
- Erkennung alter Konfigurationsstruktur
|
||||
- Migration auf die neue Struktur beim ersten Start
|
||||
- vorherige Anlage einer Sicherung mit `.bak`
|
||||
|
||||
### Legacy-Kompatibilität beim API-Key
|
||||
|
||||
Für den OpenAI-kompatiblen Provider wurde die Abwärtskompatibilität auch für bestehende API-Key-Setups beibehalten.
|
||||
|
||||
Die Auflösung erfolgt in dieser Reihenfolge:
|
||||
|
||||
1. spezifische Umgebungsvariable für den OpenAI-kompatiblen Provider
|
||||
2. bisherige Legacy-Umgebungsvariable
|
||||
3. Property-basierter API-Key
|
||||
|
||||
Dadurch brechen bestehende Setups nicht still.
|
||||
|
||||
### Strikte Provider-Validierung
|
||||
|
||||
Die Konfiguration des aktiven Providers wird vor dem eigentlichen Lauf sauber validiert.
|
||||
|
||||
Dazu gehört insbesondere die Base-URL-Prüfung:
|
||||
|
||||
- syntaktisch gültige URI
|
||||
- absolute URI
|
||||
- nur `http` oder `https`
|
||||
|
||||
Ungültige Provider-Konfiguration ist eine ungültige Startkonfiguration und verhindert den Laufbeginn.
|
||||
|
||||
---
|
||||
|
||||
## Persistenz und Nachvollziehbarkeit
|
||||
|
||||
Das bestehende Zwei-Ebenen-Modell bleibt erhalten:
|
||||
|
||||
- Dokument-Stammsatz
|
||||
- Versuchshistorie
|
||||
|
||||
V1.1 führt **keine neue Persistenz-Wahrheit** ein.
|
||||
|
||||
Die durch V1 etablierten Regeln zu Status, Retry, Zielerfolg, Proposal-Quelle und Historisierung bleiben in Kraft.
|
||||
|
||||
Die Erweiterung um mehrere Provider dient der technisch sauberen Mehrprovider-Nutzung und der konsistenten Nachvollziehbarkeit des verwendeten Providers im Rahmen der bestehenden Architektur.
|
||||
|
||||
---
|
||||
|
||||
## Qualitäts- und Stabilisierungsschritte nach der V1.1-Implementierung
|
||||
|
||||
Nach der funktionalen Einführung von V1.1 wurde der Stand zusätzlich qualitativ nachgeschärft.
|
||||
|
||||
Diese Nachschärfungen gehören zum erreichten Ist-Stand.
|
||||
|
||||
### 1. Rückwärtskompatibilität und Provider-Validierung
|
||||
|
||||
Es wurden Korrekturen durchgeführt für:
|
||||
|
||||
- Legacy-Fallback beim OpenAI-kompatiblen API-Key
|
||||
- strikte Validierung der Provider-Base-URL
|
||||
- Sicherstellung, dass alte Setups nicht still brechen
|
||||
|
||||
### 2. Dokumentation
|
||||
|
||||
Es wurde eine README für das Repository ergänzt, damit der Projektstand und die Grundbenutzung nachvollziehbar dokumentiert sind.
|
||||
|
||||
### 3. Compiler-, Test- und Build-Hygiene
|
||||
|
||||
Es wurden mehrere kleinere Qualitätskorrekturen durchgeführt, unter anderem:
|
||||
|
||||
- Bereinigung von Raw-Type-/Unchecked-Warnungen in Tests
|
||||
- Beseitigung veralteter API-Nutzung in Bootstrap-Testhilfen
|
||||
- Bereinigung des Logging-Klassenpfads im Testkontext
|
||||
- bewusste Konfiguration des Annotation Processings
|
||||
|
||||
### 4. Mutationstest- und Build-Verbesserungen
|
||||
|
||||
Die Qualität des Test- und PIT-Stands wurde gezielt verbessert, insbesondere in:
|
||||
|
||||
- `adapter-out`
|
||||
- `bootstrap`
|
||||
|
||||
Zusätzlich wurde ein PIT-Timeout bereinigt, das durch einen langsamen Integrationstest mit Netzwerkaufruf verursacht wurde.
|
||||
|
||||
### 5. Bewertung verbliebener Build-Hinweise
|
||||
|
||||
Verbleibende Log4j2-/Shade-Hinweise wurden geprüft und als funktional unkritisch eingeordnet, soweit sie keine reale Auswirkung auf den Produktivbetrieb haben.
|
||||
|
||||
---
|
||||
|
||||
## Ergebnisstatus von V1.1
|
||||
|
||||
V1.1 ist als **fertiger, implementierter und getesteter Ist-Stand** zu verstehen.
|
||||
|
||||
Das bedeutet:
|
||||
|
||||
- die Erweiterung ist umgesetzt
|
||||
- die Erweiterung ist in die bestehende Architektur eingebettet
|
||||
- die relevanten Qualitäts- und Stabilitätskorrekturen wurden nachgezogen
|
||||
- der Projektstand nach V1.1 ist gegenüber V1 fachlich stabil erweitert und technisch nachgeschärft
|
||||
|
||||
---
|
||||
|
||||
## Verhältnis zu den übrigen Repository-Dokumenten
|
||||
|
||||
Dieses Dokument ersetzt **nicht** die bestehenden Spezifikationsdokumente.
|
||||
|
||||
Es ergänzt sie um die Aussage:
|
||||
|
||||
> **Was ist nach Abschluss von V1.1 zusätzlich tatsächlich vorhanden?**
|
||||
|
||||
Für eine vollständige Einordnung bleiben weiterhin maßgeblich:
|
||||
|
||||
- `CLAUDE.md`
|
||||
- `docs/specs/technik-und-architektur.md`
|
||||
- `docs/specs/fachliche-anforderungen.md`
|
||||
- `docs/specs/meilensteine.md`
|
||||
|
||||
Dieses Dokument beschreibt den **erreichten zusätzlichen Ist-Zustand bis einschließlich V1.1**.
|
||||
|
||||
---
|
||||
|
||||
## Kompakte Zusammenfassung für KI-Systeme
|
||||
|
||||
Wenn eine KI diesen Stand kurz einordnen soll, gilt:
|
||||
|
||||
- V1 des `pdf-umbenenner` war vollständig umgesetzt und freigegeben
|
||||
- V1.1 ist eine **kleine, minimal-invasive Erweiterung**
|
||||
- V1.1 fügt **native Claude-Unterstützung** hinzu
|
||||
- der bisherige **OpenAI-kompatible Weg bleibt erhalten**
|
||||
- die Konfiguration unterstützt mehrere Provider, aber **genau einen aktiven Provider**
|
||||
- bestehende Konfigurationen bleiben per Migration und Legacy-Fallback kompatibel
|
||||
- fachliche Regeln, Architekturprinzipien und Kernverhalten aus V1 bleiben unverändert
|
||||
- nach der Implementierung wurden zusätzliche Qualitäts-, Build- und Testhygiene-Maßnahmen durchgeführt
|
||||
- der Ist-Zustand des Projekts umfasst damit **V1 + V1.1**
|
||||
@@ -248,23 +248,6 @@ public class StartConfigurationValidator {
|
||||
|
||||
// === Helper methods for common validation patterns ===
|
||||
|
||||
/**
|
||||
* Validates that a required directory path is not null, exists, and is a directory.
|
||||
* <p>
|
||||
* Used for paths like source and target folders that must already exist before processing can begin.
|
||||
*/
|
||||
private void validateRequiredExistingDirectory(Path path, String fieldName, List<String> errors) {
|
||||
if (path == null) {
|
||||
errors.add("- " + fieldName + ": must not be null");
|
||||
return;
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
errors.add("- " + fieldName + ": path does not exist: " + path);
|
||||
} else if (!Files.isDirectory(path)) {
|
||||
errors.add("- " + fieldName + ": path is not a directory: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a required file path is not null and its parent directory exists and is a directory.
|
||||
* <p>
|
||||
@@ -324,14 +307,13 @@ public class StartConfigurationValidator {
|
||||
* or exists and is a directory.
|
||||
*/
|
||||
private void validateOptionalExistingDirectory(Path directoryPath, String fieldName, List<String> errors) {
|
||||
if (directoryPath != null && !directoryPath.toString().isBlank()) {
|
||||
if (Files.exists(directoryPath)) {
|
||||
if (directoryPath != null && !directoryPath.toString().isBlank() && Files.exists(directoryPath)) {
|
||||
if (!Files.isDirectory(directoryPath)) {
|
||||
errors.add("- " + fieldName + ": exists but is not a directory: " + directoryPath);
|
||||
}
|
||||
}
|
||||
// If it doesn't exist yet, that's acceptable - we don't auto-create
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,9 +9,6 @@ import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.provider.MultiProviderConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
|
||||
@@ -28,7 +25,6 @@ import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
|
||||
*/
|
||||
public class PropertiesConfigurationPortAdapter implements ConfigurationPort {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(PropertiesConfigurationPortAdapter.class);
|
||||
private static final String DEFAULT_CONFIG_FILE_PATH = "config/application.properties";
|
||||
|
||||
private final Function<String, String> environmentLookup;
|
||||
|
||||
@@ -26,6 +26,8 @@ package de.gecheckt.pdf.umbenenner.application.port.out;
|
||||
*/
|
||||
public class DocumentPersistenceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -8362115097253107643L;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code DocumentPersistenceException} with the given message.
|
||||
*
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.service.AiResponseValidator.AiValidationResult;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.AiErrorClassification;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DateSource;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.NamingProposal;
|
||||
|
||||
@@ -1339,8 +1339,6 @@ class DocumentProcessingCoordinatorTest {
|
||||
private final CapturingDocumentRecordRepository recordRepo;
|
||||
private final CapturingProcessingAttemptRepository attemptRepo;
|
||||
boolean failOnExecute = false;
|
||||
Consumer<TransactionOperations> lastOperations = null;
|
||||
|
||||
CapturingUnitOfWorkPort(CapturingDocumentRecordRepository recordRepo,
|
||||
CapturingProcessingAttemptRepository attemptRepo) {
|
||||
this.recordRepo = recordRepo;
|
||||
@@ -1349,7 +1347,6 @@ class DocumentProcessingCoordinatorTest {
|
||||
|
||||
@Override
|
||||
public void executeInTransaction(Consumer<TransactionOperations> operations) {
|
||||
this.lastOperations = operations;
|
||||
if (failOnExecute) {
|
||||
throw new DocumentPersistenceException("Simulated transaction failure");
|
||||
}
|
||||
|
||||
@@ -1488,7 +1488,6 @@ class BatchRunProcessingUseCaseTest {
|
||||
private static class CapturingProcessingLogger implements ProcessingLogger {
|
||||
int infoCallCount = 0;
|
||||
int debugCallCount = 0;
|
||||
int debugSensitiveAiContentCallCount = 0;
|
||||
int warnCallCount = 0;
|
||||
int errorCallCount = 0;
|
||||
|
||||
@@ -1504,7 +1503,6 @@ class BatchRunProcessingUseCaseTest {
|
||||
|
||||
@Override
|
||||
public void debugSensitiveAiContent(String message, Object... args) {
|
||||
debugSensitiveAiContentCallCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${maven-failsafe-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@@ -159,9 +159,7 @@ class BootstrapRunnerEdgeCasesTest {
|
||||
|
||||
@Test
|
||||
void createRunContext_generatesUniqueRunId() throws Exception {
|
||||
// While we can't directly call the private createRunContext method,
|
||||
// we can verify its effects through BatchRunContext behavior
|
||||
StartConfiguration mockConfig = new StartConfiguration(
|
||||
new StartConfiguration(
|
||||
Files.createDirectories(tempDir.resolve("source")),
|
||||
Files.createDirectories(tempDir.resolve("target")),
|
||||
Files.createFile(tempDir.resolve("db.sqlite")),
|
||||
|
||||
@@ -136,7 +136,7 @@ class PdfUmbenennerApplicationTest {
|
||||
void applicationEntry_isStaticMain() throws Exception {
|
||||
// Verify main is static and returns void
|
||||
Method main = PdfUmbenennerApplication.class.getDeclaredMethod("main", String[].class);
|
||||
int modifiers = main.getModifiers();
|
||||
main.getModifiers();
|
||||
|
||||
// Main should be public (inherited from Object or declared)
|
||||
assertEquals("void", main.getReturnType().getSimpleName(),
|
||||
|
||||
Reference in New Issue
Block a user