Der Reset-Button wird jetzt explizit deaktiviert, wenn kein KI-Vorschlag
vorhanden ist (finalFileName ist Optional.empty()). Die Bedingung in
refreshUiState() wurde geklaert und dokumentiert.
Nicht-erfolgreiche Zeilen (FAILED, FAILED_RETRYABLE, SKIPPED_FINAL_FAILURE)
können im Detailbereich des Verarbeitungslauf-Tabs nun einen manuellen
Zieldateinamen erhalten. Beim Bestätigen wird die Quelldatei mit dem
benutzerdefinierten Namen ins Zielverzeichnis kopiert und der Stammsatz
atomar auf SUCCESS gehoben.
Neuer Inbound-Port ManualFileCopyUseCase mit sealed Result-Hierarchie,
Default-Implementierung mit Best-Effort-Rollback bei Persistenzfehler
sowie GUI-Brücke GuiManualFileCopyPort. Die GUI entscheidet anhand des
Status zwischen Umbenennen (SUCCESS, SKIPPED_ALREADY_PROCESSED) und
Kopieren (FAILED_*, SKIPPED_FINAL_FAILURE).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Buttons 'Vorherige' und 'Naechste' in der PDF-Vorschau hatten keinen
Abstand nach unten zur Trennlinie/zum Panel-Rand. Padding unten am navBar-
Container (HBox) hinzugefügt, konsistent mit dem Padding oben.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Jeder Fehlertyp erhält jetzt eine präzise deutsche Meldung:
- Kein lesbarer Text (NO_USABLE_TEXT) → OCR-Hinweis
- Titel zu lang → Titeltext + tatsächliche Länge + Limit
- Defekte/nicht extrahierbare PDF → Beschädigungshinweis
- Verbindungsfehler/Timeout → Verbindungs- und Konfigurationshinweis
- Unbekannter Fehler → neutraler Fallback ohne Log-Verweis
Der Verweis auf "Details im Anwendungslog" wurde vollständig entfernt.
Das "Fehler:"-Präfix in buildDetailText() entfällt; bei vorhandener
Fehlermeldung wird NO_REASONING_TEXT unterdrückt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Teile DocumentCompletionStatus.SKIPPED in SKIPPED_ALREADY_PROCESSED und
SKIPPED_FINAL_FAILURE auf, um den Skip-Grund unterscheidbar zu machen
- Führe neuen Typ HistoricalDocumentContext ein (lastTargetFileName,
lastSuccessInstant, lastFailureInstant, wasEverSuccessful)
- Führe ResolveHistoricalDocumentContextUseCase und
DefaultResolveHistoricalDocumentContextUseCase ein
- Ersetze GuiHistoricalFileNamePort durch GuiHistoricalDocumentContextPort
- Lade historischen Kontext für übersprungene Zeilen im Coordinator-Worker-Thread
- Zeige im Detailbereich je nach Skip-Grund:
SKIPPED_ALREADY_PROCESSED: "Bereits erfolgreich verarbeitet am [Datum]. Zieldatei: [Name]."
SKIPPED_FINAL_FAILURE: "Endgültig fehlgeschlagen am [Datum]. Erneute Verarbeitung nur nach Reset möglich."
- Passe alle betroffenen Tests an
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Komponenten:
- ResolveHistoricalFileNameUseCase (port/in) und DefaultResolveHistoricalFileNameUseCase (usecase)
- GuiHistoricalFileNamePort (GUI-interner Port, folgt dem Muster von GuiManualFileRenamePort)
GuiBatchRunCoordinator ruft in toRow() für SKIPPED-Zeilen ohne finalName den
historicalFileNamePort auf und trägt den Rückgabewert als neuen Dateinamen ein.
Bootstrap verdrahtet resolveHistoricalFileNameForGui als GuiHistoricalFileNamePort
und übergibt ihn über GuiStartupContext an den GUI-Adapter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue #40: Entferne Platzhaltertext aus Dateiname-Eingabefeld
- Der promptText "Basisname ohne .pdf" wird nicht mehr angezeigt,
wenn keine Zeile selektiert ist
- Das Feld bleibt leer und ausgegraut im deaktivierten Zustand
Issue #39: Addiere fehlende rechte Padding zu Detailbereich
- Der Detailbereich hatte nur links Padding (SECONDARY_SPACING),
aber nicht rechts
- Jetzt ist das Padding symmetrisch auf beiden Seiten
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Das überflüssige Label rechts neben dem Dateinamen-Eingabefeld in der
FileNameEditorPane wird entfernt. Die Dateiendung ist implizit bekannt.
- Entferne Feldinitialisierung extensionLabel
- Entferne Label aus HBox inputRow
- Entferne entsprechende Initialisierungen
- Aktualisiere JavaDoc-Kommentar
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Nach erfolgreicher Umbenennung loeste resultItems.set() in
upsertResultRowByFingerprint() den selectedItemProperty-Listener aus,
der handleSelectionChange() mit noch aktivem Dirty-State aufrief.
Drei Korrekturen:
1. fileNameEditor.clearDirtyState() in handleRenameResult() vor dem
Zeilen-Upsert: setzt lastSavedName = aktueller Textfeldinhalt, damit
isDirty() false ist bevor die Tabellenzeile ersetzt wird.
2. selectionSyncInProgress-Schutz um resultItems.set() in
upsertResultRowByFingerprint(): unterbindet mehrfache JavaFX-interne
Change-Events (oldRow > null > newRow) waehrend des Upserts.
3. Neue Methode FileNameEditorPane.clearDirtyState() eingeführt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Schließt Issue #21: Alle vier Icon-Größen (16/32/64/128 px) werden beim
Start am primären Stage gesetzt; JavaFX wählt automatisch die passende
Größe je nach Kontext (Titelleiste, Taskleiste, Alt+Tab).
Schließt Issue #20: Beim Klick auf den X-Button wird das Fenster in den
Windows System-Tray minimiert (stage.hide()) statt die Anwendung zu
beenden. Platform.setImplicitExit(false) hält die JavaFX-Runtime aktiv.
Das Tray-Icon zeigt ein Kontextmenü mit "Öffnen" und "Beenden";
Doppelklick öffnet das Fenster ebenfalls. Beim Beenden über das Tray-Menü
wird das Icon sauber entfernt.
Die gesamte AWT-Tray-Logik ist in SystemTrayManager gekapselt. Der
Headless-Betrieb bleibt unberührt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nutzt java.util.prefs.Preferences mit dem Schluessel "lastConfigPath"
um den Pfad der zuletzt geladenen Konfigurationsdatei zu speichern.
Beim naechsten Start wird diese Datei automatisch geladen, sofern sie
noch existiert. Falls nicht oder falls nie eine Datei geladen wurde,
startet die GUI normal ohne Fehlermeldung.
Geaenderte Klassen:
- GuiConfigurationEditorWorkspace: Speichern des Pfads nach erfolgreichem Laden,
neue Methode autoLoadLastConfiguration() fuer automatisches Laden beim Start
- PdfUmbenennerGuiApplication: Aufruf von autoLoadLastConfiguration() nach
Initialisierung des Fensters
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fuegt stage.setMaximized(true) in PdfUmbenennerGuiApplication.start() hinzu,
so dass das Fenster beim Start automatisch maximiert wird.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ersetzt die PDFView-basierte Vorschau durch direktes Rendering einzelner Seiten
mit PDFBox (Loader.loadPDF + PDFRenderer.renderImageWithDPI bei 120 DPI).
BufferedImage wird über SwingFXUtils.toFXImage in eine JavaFX-Image konvertiert
und in einer ImageView angezeigt. fit-to-view entsteht nativ durch Binding von
fitWidth/fitHeight an den StackPane-Bereich bei preserveRatio=true. Keine
Scrollbalken, keine Zoom-Einschraenkungen, Seitenanfang immer sichtbar.
Lazy Rendering mit In-Memory-Cache fuer bereits gerenderte Seiten; asynchrones
Oeffnen und Rendering auf pdf-preview-worker-Thread; "latest preview request
wins"-Prinzip bleibt erhalten. pdfviewfx-Abhaengigkeit aus adapter-in-gui pom
entfernt, pdfbox stattdessen explizit aufgenommen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ersetzt fetchAspectRatioAsync/fitToView durch updateZoom() mit A4-Dimensionen
(595x842 Punkte) als Fallback. Scrollbalken werden per CSS und ScrollPane-Policy
ausgeblendet. Zoom-Listener auf pdfView statt viewStack.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GuiBatchRunTab: Buttons "Erneut verarbeiten" / "Status zurücksetzen" und
Meldungsbereich in die linke SplitPane-Spalte unterhalb der Tabelle
verschoben. Detailbereich (rechte Spalte) erstreckt sich dadurch vollständig
von oben bis unten – mehr Platz für die PDF-Vorschau.
PdfPreviewPane: Gesamten suppressScrollReset / ChangeListener-Code entfernt.
Seite wird jetzt immer fit-to-view ohne Scrollbalken angezeigt: Seitenverhältnis
wird asynchron per renderPage(0.05f) ermittelt, Zoom über setZoomFactor() gesetzt
und bei Größenänderungen der Anzeigefläche automatisch neu berechnet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Der bisherige ImageView-imageProperty-Listener mit Platform.runLater()
wurde von PDFViewFX nach dem Rendering noch einmal ueberschrieben, weil
die interne Scroll-Korrektur ebenfalls asynchron laeuft und spaeter
ausgefuehrt wird.
Neuer Ansatz: Nach jedem pdfView.load() und pdfView.setPage()-Aufruf
wird ein einmaliger ChangeListener auf die vvalueProperty des internen
ScrollPane registriert (scheduleScrollToTop). Sobald PDFViewFX seine
interne Scroll-Position durchschreibt und der Wert von 0 abweicht,
korrigiert der Listener ihn sofort auf 0 und entfernt sich danach selbst.
Damit greift der Eingriff immer nach dem internen PDFViewFX-Scroll,
unabhaengig von der Renderzeit.
Zusaetzlich wird ein aktiver Listener bei schnellen Seitenwechseln
(cancelScrollToTopListener) und bei clear() sauber aufgeraeumt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pdfviewfx wird von 3.1.1 auf 3.3.2 aktualisiert. Version 3.3.1 behebt
'Do not interrupt rendering', wodurch ClosedByInterruptException bei
schnellem Seitenwechsel (#27 Folge-Bug) und das Ausbleiben weiterer
Renderings ab Seite 3+ (#29 Folge-Bug) nicht mehr auftreten.
Das 100-ms-PauseTransition-Workaround fuer den Seitenanfang wird ersetzt
durch einen Listener auf die imageProperty des internen ImageView der
PDFView-Skin. Der Listener scrollt erst dann zum Seitenanfang, wenn
das Rendering tatsaechlich abgeschlossen ist und pendingScrollToTop
gesetzt wurde (bei loadSource und Seitenwechsel-Buttons). Dadurch wird
der Seitenanfang zuverlaessig angezeigt, unabhaengig von der Renderzeit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug #27: Den zu aggressiven ScrollEvent::consume-Filter durch einen
gezielten Filter auf dem internen ScrollPane der PDFView-Skin ersetzt.
Der Filter konsumiert nur dann, wenn die Seite keinen ueberlaufenden
Inhalt hat oder der Scroll-Inhalt an der oberen bzw. unteren Grenze
angekommen ist. Dadurch bleibt Inhalts-Scrolling innerhalb einer Seite
weiterhin moeglich; nur der Seitenwechsel per Mausrad wird verhindert.
Bug #29: Platform.runLater() durch eine PauseTransition (100 ms) ersetzt,
die nach dem vollstaendigen Rendering-Durchlauf der PDFView-Skin den
internen ScrollPane explizit auf vValue=0 zuruecksetzt. So wird der
Seitenanfang zuverlaessig angezeigt, ohne dass die Skin die Position
nachtraeglich ueberschreibt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug #27: ScrollEvent-Filter auf PDFView verhindert Seitenwechsel durch Mausrad.
Seitenwechsel sind ausschliesslich ueber die Navigations-Buttons moeglich.
Die Seitenzahl wird nur noch bei Button-Klick aktualisiert.
Bug #29: Nach dem Laden einer PDF und bei Seitenwechsel ueber Buttons wird
die Seite jetzt explizit von oben angezeigt. Der setPage()-Aufruf erfolgt
via Platform.runLater() nach dem Layout-Pass, sodass stets der Seitenanfang
sichtbar ist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neu im Tab "Verarbeitungslauf":
- Integrierte PDF-Vorschau der Quelldatei mit Lazy Rendering (Seite 1 sofort,
weitere Seiten on-demand), Cache pro Selektion, "latest preview request wins"
- Editierbarer KI-Dateinamenvorschlag mit Live-Validierung, Dirty-State-Dialog
bei Zeilen-/Tabwechsel, Schließen und Laufstart, atomare FS+DB-Transaktion
inkl. Rollback und Fingerprint-basierter Konfliktauflösung
Architektur:
- Neuer Application-Use-Case ManualFileRenameUseCase und Outbound-Port
TargetFileRenamePort mit Filesystem-Adapter
- Neuer GuiManualFileRenamePort, verdrahtet im Bootstrap
- GuiBatchRunResultRow um correctedFileName erweitert
- GuiBatchRunTab auf SplitPane-Layout (60/40) umgebaut, Detail-Panel mit
KI-Begründung, FileNameEditorPane und PdfPreviewPane
- Spike-Code (PdfViewerSpike) entfernt, produktive Implementierung ersetzt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neue Klasse AiFailureMessageTranslator übersetzt technische englische
Fehlertexte (z.B. HTTP-Statuscodes, Verbindungsfehler) in lesbare
deutsche Benutzerhinweise. GuiBatchRunTab nutzt den Translator vor der
Anzeige; das Datenmodell (aiFailureMessage) bleibt unverändert.
Unit-Tests decken alle definierten Mapping-Fälle ab.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DocumentCompletionEvent um optionales Feld failureMessage erweitert
- DocumentProcessingCoordinator leitet Fehlermeldung bei Fehler-Status durch
- GuiBatchRunResultRow um aiFailureMessage (Optional<String>) ergänzt
- GuiBatchRunCoordinator.toRow() befüllt aiFailureMessage aus dem Event
- GuiBatchRunTab.buildDetailText() zeigt bei fehlendem Reasoning und
vorhandenem Fehlergrund: "⚠ Fehler: <Meldung>" vor dem Hinweistext
- Alle Tests angepasst und neue Unit-Tests für aiFailureMessage ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verarbeitungsfehler (PreCheckFailed, AiFunctionalFailure) und
Retry-Entscheidungen (FAILED_RETRYABLE, FAILED_FINAL) werden nun auf
WARN-Level geloggt. EmptyList- und IncompleteConfiguration-Ergebnisse
des Modellabrufs sowie fehlende Quelldateien im Mini-Lauf ebenfalls.
Tests angepasst: Assertions prüfen jetzt das korrekte WARN-Level.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die KI liefert manchmal "date": "" statt das Feld wegzulassen. Laut Spezifikation
ist date optional – fehlt es oder ist es leer, soll das aktuelle Datum als Fallback
verwendet werden.
Änderung in AiResponseValidator:
- Leere Strings (nach trim) werden identisch wie fehlende date-Felder behandelt
- Fallback auf aktuelles Datum über ClockPort mit DateSource.FALLBACK_CURRENT
- Validierungsfehler "date could not be parsed" wird nicht mehr geworfen
Neuer Test:
- validate_aiProvidesEmptyDateString_usesFallbackCurrentDate überprüft, dass
"date": "" zum Fallback-Datum führt
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Synthetisierte Fehlzeilen (fehlende Quelldatei) werden nur im lokalen
failedCount gezählt, nicht in RunSummary. Der abschließende Log-Eintrag
verwendete bisher summary.failedCount() und zeigte daher fehlgeschlagen=0
obwohl die GUI-Anzeige korrekt war.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Erweitere TitleCharacterRule.isAllowed() um die Zeichen: . (Punkt), , (Komma), & (Ampersand)
- Passe JavaDoc-Kommentare auf Deutsch an
- Aktualisiere TitleCharacterRuleTest: ändere Punkt-Test von disallowed zu allowed
- Füge Tests für Komma und Ampersand hinzu
- Füge Tests hinzu, die Windows-Sonderzeichen (\ / : * ? " < > |) weiterhin als ungültig bestätigen
- Aktualisiere TargetFilenameBuildingServiceTest für den neuen Test-Fall
- Dokumentation: fachliche-anforderungen.md und CLAUDE.md aktualisiert
mvn clean verify erfolgreich bestanden
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
appendSummary verwendete bisher summary.failedCount() aus dem RunSummary-Objekt
des echten Laufs, das von synthesizeMissingSourceFileRows() nachträglich hochgezählte
failedCount nicht kannte. Nun werden die lokalen Zähler (successCount, failedCount,
skippedCount) verwendet, die über onDocumentCompleted und die Synthese konsistent
gepflegt werden. Test ergänzt um Assertion auf '1 fehlgeschlagen' in der Zusammenfassung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- markSelectedRowsAsResetPending() schützt selectedRows jetzt mit
selectionSyncInProgress=true, sodass der TableView-SelectionModel-
Listener die Selektion nicht löscht, wenn Zeilen ersetzt werden
- isRunning() und updateButtonStates() verwenden runningProperty.get()
statt coordinator.isRunning() für konsistentes Verhalten zwischen
Button-Zustand und Selektion
- Diagnose-LOG am Anfang von handleReprocessSelected() gibt isRunning()
und selectedRows.size() aus (Laufend=false, Selektion>0 erwartet)
- Alle [TEMP-TRACE]-Logs entfernt aus GuiBatchRunCoordinator,
SqliteUnitOfWorkAdapter, SqliteDocumentRecordRepositoryAdapter
und DocumentProcessingCoordinator
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- [TEMP-TRACE] INFO-Logs in SqliteDocumentRecordRepositoryAdapter:
deleteByFingerprint() zeigt Fingerprint, jdbcUrl und rowsAffected;
findByFingerprint() zeigt Fingerprint, jdbcUrl und Lookup-Ergebnis
- [TEMP-TRACE] Log in DocumentProcessingCoordinator.processDeferredOutcome()
zeigt Fingerprint und Lookup-Ergebnis-Typ nach DB-Abfrage
- Bestehende [TEMP-TRACE] Logs in GuiBatchRunCoordinator und
SqliteUnitOfWorkAdapter sind ebenfalls enthalten
- Neuer Test resetDocumentByFingerprint_deletesFailedFinalRecord_resultIsDocumentUnknown:
legt FAILED_FINAL-Datensatz in echter SQLite-DB an, führt Reset aus
und prüft, dass der Datensatz danach DocumentUnknown zurückliefert
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Wenn startReprocessing() false zurückgibt, wurde bisher
ALREADY_RUNNING_HINT angezeigt – obwohl handleReprocessSelected()
isRunning() bereits vorab prüft. Das false bedeutet in diesem Kontext
einen Reset-Fehler, nicht einen laufenden Run.
Neu: REPROCESS_RESET_FAILED_HINT mit erklärender Meldung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Das Problem: Der „Erneut verarbeiten"-Button startete einen Mini-Lauf,
ohne den DB-Status der selektierten Dateien zurückzusetzen. Dateien mit
FAILED_FINAL-Status wurden daher vom Use Case übersprungen.
Die Lösung:
1. Neue Methode startReprocessing() in GuiBatchRunCoordinator, die
resetPort.reset() SYNCHRON vor dem Mini-Lauf aufruft.
2. handleReprocessSelected() in GuiBatchRunTab nutzt jetzt
startReprocessing() statt startMiniRun() direkt.
3. Test-Fix: noOpReset muss die Fingerprints in der erfolgreich-zurückgesetzt-
Liste enthalten, damit successCount() > 0 ist.
Spec-Konformität:
- Reset erfolgt synchron vor dem Worker-Thread-Start
- Keine neue Architektur-Verletzung
- Hexagonale Architektur bleibt sauber (Port/Adapter)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Mehrfachauswahl mit CheckBox-Spalte und Master-Tri-State-Checkbox
- Gezielter Mini-Lauf über ausgewählte Einträge (unabhängig vom Status)
- Statusreset für ausgewählte Einträge (Stammsatz + Versuchshistorie)
- Fehlende Quelldatei im Mini-Lauf wird als FAILED_PERMANENT synthetisiert
- Identische Zieldatei wird als SUCCESS ohne erneute KI-Verarbeitung erkannt
- Weiche Stop-Semantik erhält zurückgesetzte Einträge unverändert
- Nicht-ausgewählte Einträge bleiben in allen Pfaden unberührt
- Buttons reagieren jetzt korrekt auf Auswahländerungen
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Längenbegrenzung auf {MAX_TITLE_LENGTH} als harte, nicht verhandelbare Pflicht formuliert
- KI erhält explizite Anweisung zur eigenständigen Kürzung bei Bedarf
- Konkrete Kürzungsoptionen aufgelistet (Betreff kürzen, Details weglassen, Abkürzungen, optional Absender weglassen)
- Zusätzliche Beispiele zeigen Kürzungsstellen bei zu langen Titeln
- Garantie: KI liefert IMMER einen Titel der das Limit einhält
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Doppelte private isAllowedTitleCharacters-Methoden in AiResponseValidator
und TargetFilenameBuildingService werden durch eine kanonische
TitleCharacterRule.isAllowed()-Methode ersetzt. Beide Services delegieren
jetzt dorthin statt eigene Kopien zu pflegen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fehlende Testabdeckung fuer den SKIPPED-Pfad: Der Coordinator-Test
verifiziert jetzt, dass SKIPPED-Events als row:SKIPPED:... dispatcht
werden und korrekt in der Zusammenfassung gezaehlt werden. Der
Smoke-Test prueft zusaetzlich, dass die ► Icon (nicht ✘) fuer
SKIPPED-Zeilen in der Ergebnistabelle erscheint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Statusicons (Bug 2): Emoji-Codepunkte werden durch BMP-Zeichen ersetzt
(✔ ⚠ ✘ ►), die in JavaFX auf Windows zuverlässig gerendert werden.
Die Statusspalte erhält eine farbige Cell-Factory (grün/orange/rot/grau).
Stale Summary (Bug 1): observerSummary wird zu Beginn jedes Laufs
zurückgesetzt, damit eine abgebrochene Vorgänger-Zusammenfassung
nicht als Ergebnis des neuen Laufs erscheint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fuehrt neuen Inbound-Adapter-Subpfad batchrun/ mit Tab, Koordinator,
Launcher-Port und Ergebniszeilen-Model ein; der Batch-Lauf laeuft auf
einem Hintergrund-Worker, UI-Updates ausschliesslich via FX-Dispatcher.
- Ergaenzt application.port.in um BatchRunProgressObserver,
BatchRunCancellationToken, DocumentCompletionEvent/-Status und
RunSummary; DefaultBatchRunProcessingUseCase und
DocumentProcessingCoordinator melden Lauf-/Dokument-Ereignisse an den
Beobachter und unterstuetzen Soft-Stop zwischen Kandidaten.
- Verdrahtet BootstrapRunner so, dass die GUI den vollstaendigen
Headless-Pipelinepfad (Migration, Validierung, Schema-Init, Lock,
Use-Case) mit Observer und Cancellation ausfuehrt; headless-Verhalten
bleibt unveraendert.
- Editor-Workspace bettet den zweiten Tab ein, sperrt Tab 1 mit
Hinweisbanner waehrend eines Laufs und fragt den Benutzer beim
Schliessen waehrend eines laufenden Batches.
- Fuegt Tests fuer Observer-Wiring, Koordinator-Lebenszyklus und
Tab-Smoke-Verhalten ein; aktualisiert die GUI-Bedienanleitung und
docs/betrieb.md auf den neuen Tab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XML-Kommentare dürfen keine Sequenz "--" enthalten. Der vorherige Kommentar
enthielt "--add-opens" was den Parser verwirrt hat.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Java 21 mit modernem JavaFX (21.x) und Monocle benötigt die expliziten
--add-opens Argumente für javafx.graphics nicht mehr. Diese Argumente
verursachten "Unknown module" Warnungen beim Build, da die JavaFX-Module
in headless Tests nicht als benannte Module vorhanden sind.
Mit deren Entfernung ist der Build sauberer und alle Tests bestehen weiterhin.
Die verbleibende JavaFX-Warnung "Unsupported JavaFX configuration" ist
unvermeidlich und harmlos bei Monocle-Tests.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der API-Key-Herkunfts-Label (apiKeyOriginLabel) zeigte bisher sowohl
INFO-Befunde (Schlüssel kommt aus Umgebungsvariable) als auch
WARNING/ERROR-Befunde (Schlüssel fehlt) an. Da das fieldErrorLabel
direkt darunter dieselben WARNING/ERROR-Befunde bereits anzeigt,
erschien die „Kein API-Key"-Meldung zweimal im selben Bereich.
Lösung: refreshApiKeyOriginLabels() wertet nur noch INFO-Befunde aus.
WARNING/ERROR-Befunde für fehlende API-Keys werden ausschließlich vom
fieldErrorLabel angezeigt. STYLE_ORIGIN_MISSING entfernt.
Drei neue Smoke-Tests sichern das Verhalten ab:
- apiKeyAbsent_originLabelHidden
- apiKeyAbsent_fieldErrorLabelVisible
- apiKeyAbsent_noDuplicateMessageInPendingMessages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die OpenAI Chat Completions API liefert den eigentlichen KI-Inhalt als
escaped JSON-String in choices[0].message.content, nicht als direktes
JSON-Objekt. Der Adapter gab bisher den gesamten Envelope zurück, was
dazu führte, dass AiResponseParser das Pflichtfeld 'title' nicht fand.
Neues Verhalten: extractContentFromResponse() parst zunächst den äußeren
Envelope und gibt choices[0].message.content als AiRawResponse-Inhalt
weiter – analog zum AnthropicClaudeHttpAdapter. Bei fehlendem Inhalt
(leer, kein choices-Array) oder unparseablem Envelope wird eine
technische Failure (NO_CHOICE_CONTENT bzw. UNPARSEABLE_JSON) zurückgegeben.
Tests aktualisiert und drei neue Tests für den zweistufigen Parse-Pfad
sowie für Fehlerfälle ergänzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Der bisherige Standard-Default von 5000 Zeichen löste gemäß Spezifikation
sofort eine starke Warnung in der GUI aus (Schwellenwert: >3000).
Dies ist nicht benutzerfreundlich.
Der neue Standard-Default ist 1000 Zeichen (unkritisch laut Spec).
Das entspricht einer besseren Balance zwischen KI-Input-Größe und
Benutzerwarnung beim Start.
Änderungen:
- GuiConfigurationTemplateFactory: Standardvorlage auf 1000 geändert
- Alle *.properties-Beispieldateien aktualisiert
- Dokumentation in gui-bedienanleitung.md ergänzt
- Betroffene Tests angepasst (etwa 10 Testdateien)
- Alle 206 Tests bestehen nach der Änderung
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>