Log4j2 referenziert nun ${sys:log.directory} mit nutzerschreibbarem
Fallback (~/pdf-umbenenner/logs). Die System-Property wird vor dem
ersten Logger-Zugriff aus der aktiven Konfigurationsdatei gesetzt
(EarlyLogDirectoryInitializer), damit Log4j2 bereits bei der
Erstinitialisierung den korrekten Pfad kennt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#90: Neuer technischer Prüfpunkt LOG_DIRECTORY_USABLE (12. Checkpoint):
- Zeigt konfigurierten log.directory-Wert und aufgelösten absoluten Pfad
- Prüft ob Verzeichnis beschreibbar/anlegbar ist (WARNING, kein ERROR)
- Liest tatsächlichen Log-Datei-Pfad via Log4j2 LoggerContext → RollingFileAppender
- LogDiagnosticsPort als neuer Outbound-Port (application-Modul)
- Log4jLogDiagnosticsAdapter als Implementierung im bootstrap-Modul
- TechnicalTestRequest erhält logDirectory-Feld
- GuiTechnicalTestCoordinator erhält logDirectoryProvider-Supplier
#89: docs/betrieb.md – MSI-Betrieb um Pfadwarnungen erweitert:
- Warnung: relative Pfade lösen sich in schreibgeschütztes C:\Program Files\ auf
- Warnung: Backslashes in .properties werden als Java-Escape-Sequenzen interpretiert
- Betroffene Parameter mit Empfehlung zu absoluten Forward-Slash-Pfaden
- Beschreibung des neuen Log-Verzeichnis-Prüfpunkts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nach dem #78-Fix finalisiert NO_USABLE_TEXT (Foto-PDF) sofort zu FAILED_FINAL.
Die drei betroffenen E2E-Testszenarien im Bootstrap-Modul erwarteten noch
das alte FAILED_RETRYABLE-Verhalten:
- deterministicContentError_twoRuns_reachesFailedFinal umgeschrieben:
Run 1 erwartet jetzt sofort FAILED_FINAL, Run 2 erwartet SKIPPED_FINAL_FAILURE.
- skipAfterFailedFinal_thirdRun_recordsSkip umbenannt zu _secondRun_:
FAILED_FINAL ist nach einem Lauf erreicht, der Skip folgt im zweiten Lauf.
- mixedBatch_oneSuccess_oneContentError_batchOutcomeIsSuccess korrigiert:
Run 1 erwartet FAILED_FINAL (nicht FAILED_RETRYABLE), Run 2 erwartet
SKIPPED_FINAL_FAILURE (nicht einen zweiten Inhaltsfehler).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bisher wurde NO_USABLE_TEXT (kein OCR-Text im PDF) wie alle anderen
deterministischen Inhaltsfehler mit der 1-Retry-Regel behandelt und
landete beim ersten Auftreten in FAILED_RETRYABLE. Da ein Bild-Scan ohne
OCR-Text sich zwischen Läufen nicht verändert, ist ein Wiederholversuch
sinnlos – der Status muss sofort FAILED_FINAL sein.
Geändert: ProcessingOutcomeTransition erkennt NO_USABLE_TEXT als
Sonderfall und liefert ohne Retry-Prüfung FAILED_FINAL. PAGE_LIMIT_EXCEEDED
und CONTENT_NOT_EXTRACTABLE behalten die 1-Retry-Regel.
Tests angepasst: Bestehende Tests, die FAILED_RETRYABLE für NO_USABLE_TEXT
erwarteten, wurden auf das korrekte Verhalten umgestellt oder auf
PAGE_LIMIT_EXCEEDED umgeschrieben. Neue Lifecycle-Tests für NO_USABLE_TEXT
(sofort FAILED_FINAL → SKIPPED_FINAL_FAILURE) hinzugefügt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nach Bestaetigung des Verwerfen-Dialogs fehlte der Aufruf, der den
Tab-Zustand zuruecksetzt. Neue Methode discardChanges() in
GuiPromptEditorTab setzt loadedContent, dirty und Tab-Titel zurueck;
ist der Tab sichtbar, wird loadPromptAsync() sofort ausgeloest, sonst
greift der bestehende selectedProperty-Listener beim naechsten Oeffnen.
GuiConfigurationEditorWorkspace ruft discardChanges() nach positivem
Bestaedigungsdialog auf. Neuer Smoke-Test verifiziert das Verhalten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Einfuehren von GuiPromptEditorPortFactory als funktionalem Interface,
damit GuiConfigurationEditorWorkspace bei jedem Laden oder Speichern
einer Konfiguration einen passenden Port fuer den Prompt-Tab erzeugen
kann. GuiPromptEditorTab.notifyConfigurationChanged() aktualisiert Port,
Pfad und maxTitleLength und setzt Dirty-State sowie Tab-Titel zurueck.
BootstrapRunner uebergibt die Factory an GuiStartupContext. Damit werden
alle vier Symptome aus #79 behoben: leerer Tab, gesperrte Textarea,
fehlgeschlagenes Speichern und fehlender Dirty-State-Indikator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- addModules-Liste per jdeps aktualisiert: java.prefs und jdk.unsupported.desktop
neu hinzugefuegt; java.desktop, java.logging und java.xml manuell beibehalten
(reflektiv genutzt, von jdeps --ignore-missing-deps nicht erkannt)
- jdeps-Ausgabe in pdf-umbenenner-packaging/jdeps-output.txt dokumentiert
- winUpgradeUuid gesetzt (EA8D0149-1401-4D3D-A98D-A2B98DAE5495); darf nie geaendert werden
- BAT-Dateien korrigiert: referenzieren nun %~dp0PDF-KI-Renamer.exe (kein Unterverzeichnis),
passend zur appContent-Einbettung ins Installationsverzeichnis
- BAT-Dateien via appContent in den MSI-Installer eingebettet
- winShortcut=true und winShortcutPrompt=false bestaetigt (waren bereits korrekt)
- app.version=${revision} bestaetigt (war bereits korrekt nach #67)
- betrieb.md: MSI-Release-Checkliste und Pfad-Empfehlung fuer sqlite.file ergaenzt
- README-icon.md: Ist-Stand dokumentiert (icon.ico ca. 127 KB, kein Platzhalter)
- Offener Punkt fuer Marcus: Laufzeit-Verifikation ohne JDK nach manuellem MSI-Build
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neuer Tab „Prompt" in der GUI-Hauptansicht ermöglicht das Lesen, Bearbeiten
und atomare Speichern der konfigurierten KI-Prompt-Datei ohne externen Editor.
Änderungen:
- PromptSaveResult: neue sealed interface mit Saved, WriteFailed, TargetDirectoryMissing,
AtomicMoveFailed als strukturierte Ergebnistypen für savePrompt()
- PromptPort: um savePrompt(String) erweitert (nicht mehr funktional – Teststubs angepasst)
- FilesystemPromptPortAdapter: savePrompt() mit Temp-Datei im selben Verzeichnis + ATOMIC_MOVE,
kein stiller Fallback bei AtomicMoveNotSupportedException
- DefaultPromptEditorUseCase: Use-Case-Klasse mit loadPrompt(), savePrompt(),
createDefaultPromptIfMissing() als Delegation an PromptPort und ResourceCreationPort
- GuiPromptEditorPort: GUI-internes Bridge-Interface (kein hexagonaler Port)
- GuiPromptEditorTab: JavaFX-Tab mit TextArea, Dirty-State-Tracking, Speichern/Reset/Anlegen,
injizierbare threadFactory + fxDispatcher für Testbarkeit
- GuiStartupContext: um promptEditorPort erweitert; alle Backward-Compat-Konstruktoren
und blank() mit noOpPromptEditorPort() versorgt
- GuiConfigurationEditorWorkspace: promptEditorTab integriert, Tab-Wechsel-Schutz erweitert
- BootstrapRunner: buildGuiPromptEditorPort() verdrahtet FilesystemPromptPortAdapter +
DefaultPromptEditorUseCase; noOpGuiPromptEditorPort() für Blank-Start-Fälle
- Tests: DefaultPromptEditorUseCaseTest, FilesystemPromptPortAdapterTest (savePrompt),
GuiPromptEditorTabSmokeTest (headless Monocle), GuiAdapterSmokeTest auf 3 Tabs aktualisiert
- docs/betrieb.md: Prompt-Tab dokumentiert, Pfad-Auflösungstabelle ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Komponente BatchRunSummaryBanner aggregiert die Ergebnisliste nach
Laufende und zeigt je Kategorie Icon + Anzahl + Text an. Banner verschwindet
beim Start des nächsten Laufs. READY_FOR_AI, PROPOSAL_READY und PROCESSING
werden nicht gezählt (nicht im DocumentCompletionStatus-Enum enthalten);
Reset-Pending-Zeilen werden explizit ausgeschlossen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue zentrale Klasse ProcessingStatusPresentation als einzige autoritative
Quelle fuer Icons, CSS-Farben, Tooltip-Texte und Summary-Kategorielabels
aller DocumentCompletionStatus-Werte. GuiBatchRunResultRow delegiert
statusIcon() und statusColor() an diese Klasse und stellt neue Methode
statusTooltip() bereit. In GuiBatchRunTab erhalten Status-Icons Tooltips
per CellFactory; die duplizierte private statusColor()-Methode entfaellt.
Fuer FAILED_PERMANENT wird im Detailbereich ein erweiterter Erklaerungstext
gemaess Spezifikation #51 angezeigt. Unit-Tests fuer ProcessingStatusPresentation
(alle Status, Eindeutigkeit, korrekte Mapping-Werte) und statusTooltip() in
GuiBatchRunResultRowTest ergaenzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ersetzt die manuelle evolveTableColumns()-Schema-Evolution durch Flyway 10.20.1.
Die Initialisierung unterscheidet drei Faelle: leere DB (Flyway-Migration),
Bestandsschema ohne Flyway-History (Baseline nach Schema-Pruefung) und
Folgestart mit Flyway-History (idempotent). Smoke-Test-Deadlock auf Windows
durch paralleles Ausgabe-Draining des Subprozesses behoben.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neues Jenkinsfile mit pipeline-Struktur (Checkout, Version bestimmen,
Maven Build, Archive JAR, Berichte, Artefakt ablegen, Aufräumen)
- Maven-Build übergibt -Drevision=MAJOR.MINOR.BUILD_NUMBER
- Archive-Stage: Bash explizit via #!/usr/bin/env bash + set -euo pipefail,
mapfile-Prüfung bricht bei 0 oder mehr als 1 Shade-JAR mit Fehlermeldung ab
- MSI-Build als bewusst manuell dokumentiert (Kommentar im Jenkinsfile)
- MAJOR/MINOR via Jenkins-Parameter, EFFECTIVE_MAJOR/MINOR-Stub für State-Datei
- docs/betrieb.md: CI-Hinweis zum manuellen MSI-Build ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ${revision}-Property im Parent-POM eingeführt; alle Kind-POM-<parent>-Blöcke
verwenden ${revision} statt hartkodierter Version
- flatten-maven-plugin 1.6.0 in <build><plugins> des Parent-POM aktiviert
(resolveCiFriendliesOnly), sodass installierte POMs keine unaufgelösten
${revision}-Referenzen enthalten
- MANIFEST.MF des Shade-JARs enthält Implementation-Version und Implementation-Title
- app.version im Packaging-Modul auf ${revision} umgestellt (war 2.5.0)
- ApplicationVersionProvider: neue Utility-Klasse im Bootstrap-Modul liest
Implementation-Version aus MANIFEST.MF, Fallback "dev" bei ungepacktem Betrieb
- ApplicationVersionProviderTest: prüft Fallback-Verhalten im Testlauf
- .gitignore: .flattened-pom.xml-Dateien ausgeschlossen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files.readAllBytes laedt grosse PDFs vollstaendig in den Heap und
riskiert OutOfMemoryError. Die Berechnung nutzt jetzt einen
DigestInputStream mit 8-KB-Puffer in try-with-resources. Das
Hash-Ergebnis ist bitidentisch zur vorigen Implementation, die
Exception-Semantik bleibt unveraendert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bei mehrfachem Provider-Wechsel oder Modelle-Neu-Laden konnten parallele
HTTP-Threads ihre Ergebnisse in dieselbe Meldungsliste schreiben. Mit
einem AtomicLong-Generationszaehler wird vor jedem Lauf eine Generation
festgehalten; bei der UI-Auslieferung auf dem JavaFX Application Thread
wird verworfen, was nicht mehr zur aktuellen Generation gehoert. Damit
ueberschreiben veraltete Worker den UI-Zustand nicht mehr.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Statt fuer jede openConfigurationFile-Anfrage einen neuen Thread zu
starten, werden Anfragen jetzt ueber einen Single-Thread-ExecutorService
mit Daemon-ThreadFactory eingereiht. Mehrfaches Klicken auf Oeffnen
erzeugt keine konkurrierenden Worker-Threads mehr; Anfragen werden
seriell hintereinander abgearbeitet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Felder currentDocument, currentRenderer, currentSourceFile,
currentPage und totalPages werden vom Worker-Thread geschrieben und
vom JavaFX Application Thread gelesen. Das volatile-Keyword garantiert
nun die Sichtbarkeit zwischen den Threads gemaess Java Memory Model.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Dependency log4j-slf4j-impl mit hartcodierter Version wurde durch
log4j-slf4j2-impl ersetzt und nutzt jetzt einheitlich die zentrale
Versionsverwaltung im Parent-POM, konsistent zu den anderen Modulen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drei neue Architektur-Übersichten unter docs/architecture/ angelegt
(domain-overview, gui-overview, adapter-overview), die das bisher in
CLAUDE.md verstreute Detailwissen zu Paketen, Schlüsselklassen, Ports
und Bootstrap-Verdrahtung pro Modulbereich bündeln. CLAUDE.md verweist
auf die drei Dateien und enthält das Detailwissen nicht mehr selbst,
sodass Arbeit an einem Modulbereich mit CLAUDE.md plus der jeweils
passenden Übersicht auskommt. Workpackage-Liste um M14 und M15 ergänzt;
V2.9-Implementierungsstand auf Modul-/Verhaltensebene konsolidiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neues funktionales Interface FilePickerDialog eingefuehrt, das Titel,
Anfangspfad und ExtensionFilter-Liste entgegennimmt. showNativeFileChooser
wendet die Filter auf den FileChooser an. pickFile reicht die Filter
durch. Test-Stubs verwenden die aktualisierte Drei-Parameter-Signatur.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Connection wird jetzt in try-with-resources geoeffnet, sodass sie
auch dann zuverlaessig geschlossen wird, wenn setAutoCommit(false) wirft.
Rollback-Behandlung bleibt unveraendert innerhalb des inneren catch-Blocks.
Ebenfalls: korrekten Import fuer DateTimeFormatter ergaenzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Catch in ResolveHistoricalDocumentContext und ResolveHistoricalFileName
praezisiert: nur DocumentPersistenceException wird abgefangen, geloggt
(WARN) und mit leerem Optional beantwortet. Andere RuntimeExceptions
propagieren weiter zum Aufrufer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Catch von Exception auf RuntimeException eingeengt; unerwartete
Laufzeitfehler werden jetzt per logger.warn() protokolliert und
weiterpropagiert statt still verschluckt zu werden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Log4j2-API als Abhaengigkeit im Application-Modul ergaenzt. Catch von
Exception auf RuntimeException eingeengt; unerwartete Laufzeitfehler
werden jetzt per logger.warn() protokolliert und weiterpropagiert statt
still verschluckt zu werden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pendingMessages.clear() wurde aus runTechnicalTestsAction() in den
Coordinator verlagert (erste Anweisung in triggerTechnicalTests()).
Damit liegen clear() und Worker-Start auf demselben Thread (FX),
und das Race-Fenster zwischen clear() und den per Platform.runLater
zurueckgefuehrten add()-Aufrufen entfaellt.
Die fachliche Produktionssemantik (Replace beim Trigger) bleibt
identisch. JavaDoc und Kommentare wurden auf Replace-Semantik
korrigiert. Der Smoke-Test trigger_twice_accumulatesTestEntries
wurde zu trigger_twice_replacesTestEntries umbenannt und prueft
nun die Replace-Erwartung des isolierten Coordinators.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setMaxHeight(200) entfernt und VBox.setVgrow(messagesListView, ALWAYS)
gesetzt. Die ListView füllt nun den verbleibenden Platz innerhalb der
Meldungs-Card; der Button darunter bleibt davon unberührt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VBox.setVgrow(card, ALWAYS) auf dem Meldungs-Card macht die Sektion
dehnbar innerhalb von sectionsBox. Damit das VGrow überhaupt wirken kann,
wurde scrollPane.setFitToHeight(true) ergänzt – ohne diese Voraussetzung
bleibt tabContent auf seine natürliche Höhe beschränkt und das VGrow
läuft ins Leere.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die leeren Label-Spacer in Spalte 0 der Fehler- und Info-Zeilen des
fieldGrid waren managed=true (JavaFX-Default). JavaFX reservierte dadurch
pro Zeile ~20px Hoehe, selbst wenn das eigentliche Fehler-Label in
Spalte 1 unmanaged/unsichtbar war. Fuenf betroffene Zeilen (baseUrl-Fehler,
Timeout-Fehler, Modell-Fehler, API-Key-Fehler, API-Key-Herkunft) erzeugten
zusammen ~96-120px ungenutzten Leerraum unterhalb der Felder.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- VBox-Spacing in Provider-Block von 2 auf 4 Pixels erhöht (wie im Pfade-Bereich)
- Padding in der Provider-Box von 6px auf 8px erhöht (wie im createCardContainer)
- Abstände zwischen Basis-URL, Modell und API-Key sind nun einheitlich mit dem Pfade-Bereich
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Eine zweite parallele Instanz wird beim Start abgewiesen. Der Schutz
greift fuer GUI- und Headless-Pfad gleichermassen vor der Modusweiche
in BootstrapRunner.
Umsetzung als ServerSocket-Bind auf 127.0.0.1:47832: stale-lock-frei,
da das Betriebssystem den Port beim Prozessende automatisch freigibt,
robust unter Windows mit gemappten Laufwerken und UNC-Pfaden, und ohne
Konflikt mit dem bestehenden RunLockPort, der nur den Batch-Lauf
schuetzt. Bei kollidierender Bindung erscheint im GUI-Modus ein
Swing-Dialog (JavaFX ist hier noch nicht initialisiert) und im
Headless-Modus eine Logmeldung; beide Pfade enden mit Exit-Code 1.
Ein ShutdownHook und try-with-resources geben den Port deterministisch
wieder frei.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wenn die KI einen Titel nahe am Zeichenlimit erzeugt, kam es regelmaessig
zur Limit-Ueberschreitung um wenige Zeichen, was den Lauf in
FAILED_RETRYABLE und nach Wiederholung in FAILED_FINAL trieb.
Der Default-Prompt enthaelt jetzt eine explizite Kuerzungsanweisung
(Abkuerzungen, kompaktere Datumsformate, Weglassen unwichtiger Details)
mit dynamisch eingesetztem Zeichenlimit. Die bestehende Validierung
in AiResponseValidator bleibt als Sicherheitsnetz unveraendert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>