Commit Graph

301 Commits

Author SHA1 Message Date
marcus c2a7921675 Scheduler-Ports und Typen im Application-Modul anlegen (Schritt 3)
Neue Typen (port/in):
- SchedulerControlUseCase – Inbound-Port: start(), stop(), getStatus()
- SchedulerState – Enum: STOPPED, STARTING, RUNNING_IDLE, RUNNING_BATCH_ACTIVE, STOPPING_BATCH_ACTIVE
- SchedulerStatus – Immutable Record mit AtomicReference-ready Snapshot
- SchedulerStartException – Unchecked Exception für Start-Fehler

Neue Typen (port/out):
- RunLockHandle – AutoCloseable für tryAcquire() in try-with-resources
- RunSummary – Aggregierte Lauf-Ergebniszähler (success/failed/skipped)
- BatchRunTrigger – @FunctionalInterface für synchronen Lauf-Trigger
- BatchRunTriggerResult – Sealed Interface: Started, SkippedBusy, Failed
- SchedulerConfig – Betriebskonfiguration (intervalSeconds >= 30)
- SchedulerSettings – Persistierte Properties-Werte mit Defaults
- SchedulerPort – startScheduler() / stopScheduler()
- ConfigurationFileLockPort – acquireLock() / releaseLock() / isLocked()
- ConfigurationFileLockException – Unchecked bei Lock-Erwerb-Fehler
- SchedulerSettingsPort – loadSettings() / saveEnabled() / saveIntervalSeconds()
- SchedulerSettingsWriteException – Unchecked bei Schreib-Fehler

Erweiterungen:
- RunLockPort: neue Methode tryAcquire() → Optional<RunLockHandle>
- FilesystemRunLockPortAdapter: implementiert tryAcquire() atomar via
  CREATE_NEW; idempotentes Handle via AtomicBoolean

Test-Fixes:
- 9 Mock-Klassen in application- und bootstrap-Tests um tryAcquire()
  ergänzt (liefern Optional.empty(), da nur blockierender Pfad getestet)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 12:42:42 +02:00
marcus 93a2473c36 Neues Maven-Modul pdf-umbenenner-adapter-in-scheduler anlegen
Erzeugt das Modul-Gerüst für den autonomen Scheduler-Adapter:
- pom.xml mit Abhängigkeit auf pdf-umbenenner-application (kein Bootstrap,
  kein JavaFX, kein Shade-Plugin); flatten-maven-plugin und PIT werden mit
  bewusstem Kommentar vom Parent geerbt; JaCoCo-Schwellwerte temporär auf 0
- package-info.java für das Paket de.gecheckt.pdf.umbenenner.adapter.in.scheduler
- SchedulerPlaceholder.java als temporäre Kompilierplatzhalter-Klasse
- Modul in der Parent-pom.xml zwischen adapter-in-gui und adapter-out registriert

Die Abhängigkeitsrichtung (adapter-in-scheduler → application → domain)
verhindert den zyklischen Bezug: Bootstrap wird in einem späteren Schritt
auf dieses Modul angewiesen sein, nicht umgekehrt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 12:21:05 +02:00
marcus 791499169f Spezifikation für V3.2 hinzugefügt 2026-05-06 12:11:39 +02:00
marcus 407f1e0422 Bootstrap-Refactoring: Init/Run-Trennung mit ApplicationRunContext
Führt ApplicationRunContext als package-private Record ein, der beim
GUI-Start einmalig aus der validierten Konfiguration gebaut wird
(migrate → load → validate → schema-init). Das Ergebnis wird in
guiApplicationRunContext gecacht und von launchGuiBatchRun,
launchGuiMiniBatchRun und resetDocumentStatusForGui wiederverwendet,
sodass die Init-Sequenz nicht bei jedem Lauf wiederholt wird.

GuiStartupContext erhält das neue Feld applicationContextError
(Optional<String>), das einen deutschen Fehlertext trägt, wenn der
Kontext bei Startup nicht initialisiert werden konnte. Alle bisherigen
Konstruktoren und die blank()-Fabrik wurden rückwärtskompatibel
ergänzt.

Der Test-Helfer runnerWithGuiFactory wirft jetzt
ConfigurationLoadingException statt AssertionError, damit
initializeApplicationRunContext() den Fehler gracefully abfangen
und in applicationContextError speichern kann.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 12:07:39 +02:00
marcus ca26d181f3 Freigabe-Doku für V3.1 um Buildnummer ergänzt 2026-05-06 07:55:28 +02:00
marcus eae2472b7e Abschluss-Dokumentation V3.1: betrieb.md, Bedienanleitung, Freigabedokument
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 07:45:32 +02:00
marcus 735b3af09f Erlaube .db-Endung im FileChooser „Neue Datenbank anlegen"
Der Filter akzeptiert jetzt *.db und *.sqlite. Der vorgeschlagene
Dateiname übernimmt die Endung der aktuell konfigurierten DB-Datei
(neue-datenbank.db bzw. neue-datenbank.sqlite).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 07:31:09 +02:00
marcus 3876e647b2 Lege neue leere SQLite-Datenbank atomar via Use-Case und GUI an
Neuer Menüpunkt „Datenbank → Neue Datenbank anlegen…" mit FileChooser,
normalisierter Pfadprüfung gegen die aktive DB, gesammelter Überschreib-
Bestätigung, DB-Busy-Sperre auf Verlauf-Tab, Flyway-Migration auf den
neuesten Stand gegen eine Temp-Datei, Verbindungstest, atomarem Move
(ATOMIC_MOVE + REPLACE_EXISTING) und Umstellen der aktiven DB-Referenz
über einen neuen ActiveDatabaseContextPort. Konfig-Tab wechselt nach
Wechsel automatisch in den Dirty-State; Hinweismeldung mit Speichern-
Aufforderung wird im zentralen Meldungsbereich angezeigt.

Architektur entspricht Fall B aus der Spezifikation: Bootstrap hält den
Override prozessweit und verwendet ihn in resolveActiveJdbcUrl statt
des Werts aus der .properties-Datei. Bei Fehlern wird die Temp-Datei
zuverlässig entfernt; die aktive DB bleibt unverändert in Betrieb.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 16:52:54 +02:00
marcus 90d95b9ff8 Zentriere PDF-Vorschau via viewStack-Mindestgröße statt Timing-Hacks
Korrekte Ursachenanalyse: Im Zoom-Modus schrumpft der viewStack auf
Inhalts-Größe (ImageView). Ist der Inhalt kleiner als der Viewport,
positioniert ScrollPane den viewStack links/oben – setHvalue(0.5) ist
wirkungslos, weil nichts zu scrollen ist. Alle bisherigen runLater/
ChangeListener/AnimationTimer-Ansätze haben am falschen Hebel gedreht.

Korrekter Fix: viewportBoundsProperty-Listener im Konstruktor zwingt
viewStack auf mindestens Viewport-Größe. Pos.CENTER zentriert dann
die ImageView automatisch, wenn sie kleiner ist; bei größerem Inhalt
bleibt die Mindestgröße wirkungslos und der ScrollPane scrollt normal.

Ersatzlos entfernt: AnimationTimer-Block in applyZoom (wasInFitMode-
Zweig), Folge-Schritt-runLater (else-Zweig), setHvalue(0.5)/setVvalue(0.5)
in resetToFitView. Bindings in resetToFitView bleiben unverändert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 16:15:44 +02:00
marcus 661894f1ec Zentriere ersten Zoom-Schritt mittels AnimationTimer-Single-Shot
ChangeListener auf hvalueProperty feuert nicht zuverlässig: wenn hvalue
im Fit-Modus bereits 0.5 (oder identisch zum Reset-Wert) ist, gibt es
keine Wertänderung beim setFitToWidth(false), und der Listener läuft
nie an – der spätere JavaFX-eigene Reset auf 0.0 bleibt unkontrolliert.

AnimationTimer.handle() läuft einmal pro JavaFX-Frame, nach allen
Layout-, CSS- und Pulse-Passes des aktuellen Frames. Das ist der einzige
in JavaFX zuverlässige Mechanismus, um nach allem zu feuern, was JavaFX
in diesem Frame noch erledigt. stop() im ersten handle() macht den Timer
zum Single-Shot.

Folge-Zoom-Schritte (wasInFitMode == false) bleiben unverändert mit
einfachem Platform.runLater.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:03:29 +02:00
marcus 0651fcb6eb Fange JavaFX-Reset von hvalue mit ChangeListener ab statt per Timing
Beim Verlassen des Fit-Modus resettet JavaFX hvalue mehrfach auf 0.0,
auch nach unserem Platform.runLater-Aufruf. Verschachtelte runLater
können diesen Reset nicht zuverlässig überholen.

Lösung: Single-Shot-ChangeListener auf hvalueProperty. Er feuert beim
Reset, entfernt sich selbst und postet erst dann setHvalue(0.5)/
setVvalue(0.5) – garantiert nach dem Reset, ohne Timing-Annahmen.

Folge-Zoom-Schritte (wasInFitMode == false) bleiben unverändert mit
einfachem runLater.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:51:35 +02:00
marcus b62db18f0c Verschachtele runLater in applyZoom für zentriertes Verhalten beim ersten Zoom
Beim Verlassen des Fit-Modus (setFitToWidth(false)) löst JavaFX einen
H/V-Reset auf 0.0 aus, der innerhalb desselben Pulses passiert wie unser
setHvalue(0.5)-Aufruf im einfachen Platform.runLater. Resultat: Der Reset
überschreibt unseren Wert, die PDF springt links/oben bündig.

Lösung analog zu resetToFitView: doppelt verschachteltes runLater. Das
erste runLater stößt den Layout-Pass nach setFitToWidth(false) an; das
zweite feuert im darauffolgenden Pulse, wenn alle Layout-Folgen
abgeschlossen sind und setHvalue(0.5)/setVvalue(0.5) zuverlässig wirken.

Folge-Zoom-Schritte (wasInFitMode == false) bleiben mit einfachem
runLater und bewahren die aktuelle Scroll-Position.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:39:27 +02:00
marcus 3fb511601c Korrigiere Reihenfolge in resetToFitView für zuverlässige Zentrierung
Zwei zusammenwirkende Ursachen für die linksbündige Anzeige nach Zoom-Reset:

1. Die Property-Bindungen wurden vor setFitToWidth(true) gesetzt. Zu diesem
   Zeitpunkt sizet der viewStack noch nach der zoom-großen ImageView, sodass
   die Bindungen die imageView an die Zoom-Breite gekoppelt haben statt an
   die Viewport-Breite.

2. Verbleibende H/V-Werte aus Pan-/Zoom-Modus (insbesondere hvalue=0.0 nach
   Pan zum linken Rand) wurden nicht zurückgesetzt. Bei minimalsten
   Rounding-/Border-Differenzen wirkt hvalue auch im fit-aktiven Modus und
   richtet den Content links bündig aus.

Fix: setFitToWidth/Height(true) sofort; Bindings und setHvalue(0.5)/
setVvalue(0.5) im Platform.runLater nach abgeschlossenem Layout-Pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:26:31 +02:00
marcus a8d8a4a3c1 Kalibriere zoomLevel beim Verlassen des Fit-Modus auf visuellen Skalierungsfaktor
Beim ersten Zoom-Schritt sprang die ImageView abrupt von der visuell
sichtbaren Breite (durch fitHeight aspekt-erhaltend verkleinert) auf
naturalViewportWidth × zoomLevel, weil zoomLevel mit dem Wert 1.0 nicht
zur tatsächlich angezeigten Skalierung passte und gleichzeitig setFitHeight(0)
die Höhenrestriktion entfernte.

applyZoom() initialisiert nun beim Verlassen des Fit-Modus zoomLevel
auf currentVisualWidth / naturalImageWidth (= aktueller visueller
Skalierungsfaktor) und setzt naturalViewportWidth auf die natürliche
Bildbreite. Damit entspricht zoomLevel = 1.0 der pixel-genauen
Originaldarstellung. Der vom Caller intendierte Delta-Schritt wird vor
der Kalibrierung gesichert und nach der Kalibrierung auf den neuen
zoomLevel re-appliziert, damit applyZoom(zoomLevel + 0.10) nicht
unverändert auf den kalibrierten Wert feuert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:16:25 +02:00
marcus 3ef8fd0dc3 Imports aufgeräumt 2026-05-05 14:56:16 +02:00
marcus 265b807263 Entferne wirkungslosen H/V-Workaround in resetToFitView
Bei aktivem fitToWidth/fitToHeight hat der ScrollPane keinen scrollbaren
Bereich – setHvalue(0.5)/setVvalue(0.5) sind in diesem Zustand wirkungslos.
Die Wiederherstellung der Property-Bindungen fitWidth/fitHeight an viewStack
versetzt den ImageView in exakt denselben Zustand wie nach der initialen
Konstruktor-Initialisierung. Der StackPane zentriert dann automatisch
über die bereits gesetzte Pos.CENTER-Ausrichtung.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:51:08 +02:00
marcus b4f2bf60c6 Verschachtele Platform.runLater in resetToFitView für zuverlässige Zentrierung
Ein einzelner Platform.runLater-Aufruf kann feuern, bevor JavaFX das Layout
nach setFitToWidth(true) vollständig abgeschlossen hat. Durch Verschachtelung
eines zweiten runLater werden setHvalue(0.5) und setVvalue(0.5) erst nach dem
nächsten vollständigen Layout-Pass gesetzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:39:32 +02:00
marcus 15ff034a2b Behebe Zoom-Sprung und Zentrierung nach Rauszoomen
Bug 1: deltaY vor Akkumulation auf einen Notch-Wert begrenzen.
Plattformspezifische Scroll-Multiplikatoren (Windows-Mausgeschwindigkeit,
hohe DPI-Mäuse) können Werte wie 120 statt 40 liefern. Ohne Normierung
akkumuliert sich ein Überlaufwert, der Folge-Events sofort auslöst.

Bug 2: resetToFitView() setzt nach setFitToWidth(true) explizit
scrollPane.setHvalue(0.5) und setVvalue(0.5) (nach layout()-Aufruf),
damit vorherige Pan-Scroll-Werte die Zentrierung nicht nachwirken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:31:29 +02:00
marcus 9c27e4df01 Implementiere PDF-Vorschau: Zoom-Verbesserungen und Grab & Pan
32c: ScrollPane.setPrefSize(0,0) und StackPane.setMinSize(0,0) verhindern,
dass der Vorschaubereich beim manuellen Zoom mitwächst.

32a: Zoom-Akkumulator nutzt if statt while – pro Mausrad-Raste wird genau
eine Zoom-Stufe (10 %) angewendet, auch bei großen deltaY-Werten.

32b: Beim ersten Zoom-Einstieg wird die Ansicht auf die Bildmitte
zentriert (H/V = 0.5). scrollPane.layout() vor der Scroll-Wert-
Restaurierung stellt sicher, dass die neuen Inhaltsgrenzen bekannt sind.

32d: Grab & Pan – im manuellen Zoom-Modus kann die Vorschau mit der Maus
verschoben werden. OPEN_HAND-Cursor signalisiert den Zoom-Modus,
CLOSED_HAND die aktive Pan-Geste.

32e: resetToFitView() setzt Pan-Zustand und Mauszeiger zurück, sodass
beim Laden einer neuen Datei der Fit-to-View-Modus vollständig
wiederhergestellt wird.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:09:44 +02:00
marcus 0412874f08 #88 + #77: Fehlerursache-Übersetzung und vollständige Tooltip-Abdeckung
Aufgabe 1 (#88): AiFailureMessageTranslator auf public gesetzt, damit der
Verlauf-Tab die technischen Fehlermeldungen in benutzerfreundliche deutsche
Texte übersetzen kann.

Aufgabe 2 (#77): Vollständige Bestandsaufnahme aller interaktiven GUI-Elemente.
13 neue Konstanten in GuiTooltipTexts ergänzt (Provider-Felder, Verarbeitungs-
limits, optionale Pfade, Vorschau-Navigation, Prompt-Buttons, Dateiname-Textfeld).
Alle fehlenden Tooltips in GuiConfigurationEditorWorkspace, GuiPromptEditorTab,
PdfPreviewPane und FileNameEditorPane gesetzt. Hartcodierte Strings in
GuiPromptEditorTab durch Konstantenreferenzen ersetzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 13:25:56 +02:00
marcus 6c2e2efe22 #86: Mehrfachauswahl im Verlauf-Tab (SelectionMode.MULTIPLE)
Strg+Klick, Shift+Klick und Strg+A (alle sichtbaren Eintraege) werden durch
JavaFX natuerlich unterstuetzt. Aktionsbuttons (Reset, Loeschen) arbeiten nun
auf allen selektierten Eintraegen. Bei Status-Reset wird ein Hinweis angezeigt,
wenn SUCCESS-Eintraege in der Auswahl enthalten sind (Partial-Success-Dialog).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:45:23 +02:00
marcus 9f222208c0 #82: Live-Filter im Verlauf-Tab mit 300-ms-Debounce
Das Suchfeld löst loadOverview() nach 300 ms Tippinaktivität automatisch aus
(PauseTransition). Enter-Taste stoppt den Timer und sucht sofort. So wird die
Tabelle live gefiltert, ohne bei jedem Tastendruck eine DB-Anfrage zu starten.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:35:30 +02:00
marcus beade6ba2e #32: Mausrad-Zoom (Strg+Rad) in PDF-Vorschau ergänzt
Strg + Mausrad ändert den Zoomfaktor in 10-%-Stufen (Bereich 10–500 %).
Beim ersten Zoom verlässt die Vorschau den Fit-to-View-Modus; das ScrollPane
übernimmt dann die Scrollbarkeit. Laden einer neuen Datei setzt den Zoom
automatisch auf Fit-to-View zurück.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:31:12 +02:00
marcus 1ffd565bd7 #80: Dirty-Indikator im Tab-Titel Konfiguration ergaenzen
refreshHeader() setzt Tab-Titel auf '* Konfiguration' wenn editorState dirty ist.
Dialog bei Neu/Oeffnen/Schliessen war bereits vorhanden (unsavedChangesGuard).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:21:32 +02:00
marcus e8732d749a #77: Fehlende Tooltips ergaenzt (Bestandsaufnahme + vollstaendige Umsetzung)
GuiTooltipTexts: neue Konstanten fuer Batchrun-Buttons, Verlauf-Spalten,
KI-Begruendung, Fehlerbereich, Modell-Neu-Laden, Browser-Button, Prompt-Textarea.
Spaltenkopf-Tooltips via Label-als-Graphic-Pattern in GuiHistoryTab und
GuiBatchRunTab; Buttons in allen Tabs beruecksichtigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:15:44 +02:00
marcus 5a97979585 #83: KI-Begruendung bei leerem Reasoning als promptText anzeigen
showReasoning() nutzt setText("") + setPromptText() statt sichtbarem Fuelltext,
damit leere Begründung klar als erwarteter Zustand erkennbar ist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:05:26 +02:00
marcus 0fd0349a78 #81: Enum-Rohnamen durch deutsche Anzeigetexte ersetzen
displayTextFor(ProcessingStatus) in ProcessingStatusPresentation ergaenzt.
Status-ComboBox als ComboBox<ProcessingStatus> mit StringConverter umgestellt;
Versuche-Tabelle und Detail-Statuslabel zeigen nun Anzeigetext statt Enum-Namen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:01:44 +02:00
marcus 5129d3c9f6 #84: Aktionsbuttons im Verlauf-Tab nach Laufende reaktivieren
notifyRunEnded() in GuiHistoryTab ergänzt; GuiConfigurationEditorWorkspace
verdrahtet batchRunTab.runningProperty() und ruft notifyRunEnded() via
Platform.runLater() auf, sobald der Lauf endet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:54:48 +02:00
marcus cec3b4fb84 #88: Fehlerursache bei FAILED_FINAL im Verlauf-Tab anzeigen (Fall A)
Schema-Analyse ergab Fall A: failure_message ist bereits in V1 vorhanden
und wird persistiert. Keine Flyway-Migration notwendig.

- GuiHistoryTab: TextArea 'Fehlerursache' ergaenzt; zeigt failure_message
  des letzten Fehler-Attempts bei FAILED_FINAL, FAILED_RETRYABLE,
  SKIPPED_FINAL_FAILURE; promptText-Platzhalter bei NULL/leer
- SqliteProcessingAttemptRepositoryAdapter: 1000-Zeichen-Limit fuer
  failure_message vor Persistierung erzwungen (mit Kuerzungsmarkierung)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:46:37 +02:00
marcus 38b2d8c3b2 #91: Lock-File relativer Pfad – zweistufige Fallback-Strategie
Absoluter konfigurierter Pfad wird direkt verwendet (kein Fallback).
Relativer oder fehlender Pfad wird zweistufig aufgeloest:
1. Relativ zum JAR-Verzeichnis (CodeSource.getLocation())
2. Fallback auf user.home
Der final verwendete Pfad wird auf INFO-Ebene geloggt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:38:03 +02:00
marcus 9c49fc61c0 Spezifikation für V3.1 angelegt 2026-05-05 10:59:57 +02:00
marcus 406eac80e4 Commit- und Push-Pflicht nach jeder Implementierung in Arbeitsweise ergaenzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.0.239
2026-05-05 07:56:42 +02:00
marcus 4fba3379b9 V3.0 freigegeben: Build 3.0.238, MSI PDF-KI-Renamer-3.0.238.msi 2026-05-05 07:31:56 +02:00
marcus 9307a18e04 #92: jdk.crypto.ec und jdk.crypto.cryptoki zu jlink-Modulliste ergaenzt
Im MSI-Betrieb schlug jede HTTPS-Verbindung zum KI-Endpoint mit
"handshake_failure" fehl. Ursache: jdeps --ignore-missing-deps
erkennt dynamisch geladene JVM-interne Module nicht. Das volle JDK
enthaelt jdk.crypto.ec (ECDH/ECDSA-Cipher-Suites fuer TLS 1.2/1.3)
und jdk.crypto.cryptoki (PKCS#11-Provider) immer; das per jlink
erzeugte Minimal-JRE im MSI-Installer enthielt sie nicht.

Fix: beide Module explizit in <addModules> aufgenommen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 19:17:34 +02:00
marcus 6a5ae4e7b0 #89: Log-Datei landet im MSI-Betrieb verlaesslich auf der Platte
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>
2026-05-04 17:52:35 +02:00
marcus 479d176536 #89 #90: Log-Verzeichnis-Prüfpunkt + betrieb.md MSI-Pfadwarnungen
#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>
2026-05-04 17:02:51 +02:00
marcus bd2be347f6 #78: E2E-Tests auf sofortiges FAILED_FINAL fuer NO_USABLE_TEXT angepasst
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>
2026-05-04 15:37:36 +02:00
marcus 18f9c33bbb #78: NO_USABLE_TEXT (Foto-PDF) finalisiert sofort zu FAILED_FINAL
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>
2026-05-04 15:08:01 +02:00
van Elst, Marcus 349ee69a7f #85: Verwerfen im Prompt-Tab setzt Dirty-State und laedt Inhalt neu
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>
2026-05-04 12:31:18 +02:00
van Elst, Marcus 3b3e997d13 Merge fix #79: GuiPromptEditorTab Pfad-Übergabe 2026-05-04 11:46:59 +02:00
van Elst, Marcus ddfbf9b8cb #79: GuiPromptEditorTab erhaelt Konfigurationsaenderungen via notifyConfigurationChanged
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>
2026-05-04 11:38:42 +02:00
marcus 0b69adf8c9 PIT-Mutation-Coverage und Cucurrent Builds konfiguriert 2026-05-03 08:13:29 +02:00
marcus 31c65fb9fd #68: Jenkinsfile-Fixes (maven-tool, cleanWs, doppelter Checkout) 2026-05-03 07:45:56 +02:00
marcus 4ee0923721 Freigabedokument V3.0 angelegt 2026-05-03 07:39:19 +02:00
marcus 4b89743404 Bedienanleitung um Verlauf-Tab, Prompt-Tab, Statuszeile und Summary-Banner ergaenzt 2026-05-03 07:35:47 +02:00
marcus 6e03093ce9 Architektur-Uebersichten um neue Ports, Use-Cases, Adapter und GUI-Komponenten ergaenzt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:29:07 +02:00
marcus 51d6168697 #65: MSI-Vorbereitung - jdeps-Modulliste, winUpgradeUuid, BAT-Dateien, Pfad-Hinweise
- 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>
2026-04-30 14:08:53 +02:00
marcus 46fc1d4fa4 #7: Historien-Tab mit Liste, Detail, Filter, Status-Reset und Eintrag-Loeschen
Implementiert den vollstaendigen Historien-Tab (Verlauf) als vierten Tab der GUI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 13:57:07 +02:00
marcus 5d5dee0bbf #71: Prompt-Editor-Tab in der GUI implementieren
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>
2026-04-30 13:13:47 +02:00
marcus 4f5ce4c750 #50: Statuszeile mit Version, Provider und Konfigurationsdateipfad
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 12:35:21 +02:00