Spezifikation für V3.1 angelegt
This commit is contained in:
@@ -0,0 +1,924 @@
|
||||
# V3.1 – UX-Polish und Verlauf-Tab-Reife
|
||||
|
||||
**Status:** Zur Implementierung freigegeben
|
||||
**Erstellt:** 2026-05-05
|
||||
**Überarbeitet:** 2026-05-05 (nach ChatGPT-Review Runden 1, 2 und 3)
|
||||
**Autor:** Marcus (mit Claude als Mentor)
|
||||
|
||||
---
|
||||
|
||||
## Ziel
|
||||
|
||||
V3.1 ist der konsequente Nachschlag zu V3.0: Was der Produkttest aufgedeckt hat,
|
||||
wird hier bereinigt. Kein großes Architektur-Feature, kein neues Maven-Modul –
|
||||
**gezielter UX-Schliff und Robustheit**.
|
||||
|
||||
Schwerpunkte:
|
||||
|
||||
1. **Polieren** – sichtbare Schwächen aus dem V3.0-Produkttest beheben
|
||||
(#77, #80, #81, #83, #84, #88, #91)
|
||||
2. **Verlauf-Tab reifen lassen** – Suche, Mehrfachauswahl, DB-Neuanlage
|
||||
(#82, #86, #87)
|
||||
3. **Quick Win** – Mausrad-Zoom im PDF-Viewer als kleiner,
|
||||
wertvoller Gebrauchskomfort (#32)
|
||||
|
||||
Die fachliche Kernverarbeitung bleibt vollständig unverändert.
|
||||
|
||||
---
|
||||
|
||||
## Einordnung
|
||||
|
||||
V3.0 ist der abgeschlossene Ausgangspunkt. Hexagonale Architektur,
|
||||
Modulstruktur, headless-Betrieb, `.properties`-Konfigurationswahrheit
|
||||
und Flyway-DB-Evolution bleiben unangetastet.
|
||||
|
||||
V3.1 fügt **kein neues Maven-Modul** hinzu.
|
||||
|
||||
**Headless-Betrieb:** Der `adapter-in-cli`-Pfad erhält keine neue Bedienfunktion.
|
||||
Er ist jedoch von der globalen Lock-File-Pfadauflösung (#91) und einer
|
||||
ggf. notwendigen Flyway-Schemamigration (#88) betroffen – beide Änderungen
|
||||
wirken beim Programmstart, unabhängig von GUI oder CLI.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
### In V3.1 enthalten
|
||||
|
||||
| # | Thema | Kategorie |
|
||||
|---|---|---|
|
||||
| #77 | Fehlende Tooltips | UX |
|
||||
| #80 | Dirty-Indikator für Konfigurations-Tab | UX |
|
||||
| #81 | Enum-Werte statt deutscher Bezeichnungen (Status-ComboBox + Versuche-Tabelle) | UX |
|
||||
| #82 | Verlauf-Tab: Live-Filter bei Suche | GUI |
|
||||
| #83 | KI-Begründung bei SUCCESS-Versuch verwirrend leer | UX |
|
||||
| #84 | Aktionsbuttons nach Laufende nicht sofort reaktiviert | Bug |
|
||||
| #86 | Mehrfachauswahl im Verlauf-Tab (Strg+A, Strg+Klick, Shift+Klick) | GUI |
|
||||
| #87 | Neue leere SQLite-Datenbank anlegen | GUI |
|
||||
| #88 | FAILED_FINAL-Einträge zeigen keine Fehlerursache im Verlauf-Tab | UX |
|
||||
| #91 | Lock-File relativer Pfad – Fallback wie Log-Verzeichnis | Robustheit |
|
||||
| #32 | Mausrad-Zoom in PDF-Vorschau | GUI |
|
||||
|
||||
### Explizit nicht in V3.1
|
||||
|
||||
- Automatischer Scheduler / Quellordner-Überwachung (#22) → V3.x
|
||||
- PDF-Viewer Render-DPI (#23) → V3.2
|
||||
- F1-Hilfe (#69) → V3.2
|
||||
- Dark Mode (#70) → V3.x
|
||||
- Log-Viewer in der GUI (#72) → V3.2
|
||||
- Token- und Kosten-Tracking (#74) → V3.2
|
||||
- Excel-Export (#75) → V3.2
|
||||
- Automatische Update-Prüfung (#76) → V3.2
|
||||
- Änderung der fachlichen Kernverarbeitung
|
||||
- Neue Maven-Module, neue KI-Provider, Architekturbrüche
|
||||
|
||||
---
|
||||
|
||||
## Unverrückbare Leitplanken (unverändert gegenüber V3.0)
|
||||
|
||||
- Java 21, Maven Multi-Module, hexagonale Architektur
|
||||
- Shade-JAR als primäres Distributionsartefakt
|
||||
- GUI ist Standardstart, `--headless` bleibt vollständig erhalten
|
||||
- `.properties` bleibt die einzige Konfigurationswahrheit
|
||||
- Kein Webserver, kein Applikationsserver
|
||||
- GUI offiziell nur unter Windows; headless für Windows Server / Task Scheduler
|
||||
- JavaFX-Threading: I/O auf Worker-Thread, UI-Updates via `Platform.runLater()`
|
||||
- Kein JavaFX in Domain oder Application
|
||||
- JavaDoc-Standard für alle neuen öffentlichen Ports, Use-Cases, DTOs und Adapter-Methoden
|
||||
- Notwendige Code-Kommentare auf Deutsch; Logging auf Deutsch
|
||||
- Flyway ist die einzige Schema-Evolutionsquelle (kein manuelles DDL im Code)
|
||||
|
||||
---
|
||||
|
||||
## Status-Mapping-Tabelle (unverändert gegenüber V3.0)
|
||||
|
||||
Diese Tabelle ist weiterhin die einzige autoritative Quelle für Status-Darstellung
|
||||
in der GUI. Sie gilt verbindlich für alle V3.1-Features, die Statuswerte anzeigen –
|
||||
insbesondere #81 (Status-ComboBox, Versuche-Tabelle).
|
||||
|
||||
**Alle acht Statuswerte müssen vollständig unterstützt werden.**
|
||||
Kein Enum-Rohname darf für Endnutzer sichtbar sein.
|
||||
|
||||
| Domain-Status (`ProcessingStatus`) | GUI-Icon | Farbe | GUI-Text (Tooltip) | Summary-Kategorie |
|
||||
|---|---|---|---|---|
|
||||
| `SUCCESS` | `✓` | Grün | „Erfolgreich verarbeitet und umbenannt." | erfolgreich |
|
||||
| `FAILED_RETRYABLE` | `↻` | Orange | „Temporärer Fehler – wird beim nächsten Lauf automatisch erneut versucht." | wird wiederholt |
|
||||
| `FAILED_FINAL` | `×` | Rot | „Dauerhaft nicht verarbeitbar – z. B. kein Textinhalt (Foto-PDF), Passwortschutz oder beschädigte Datei. Kein weiterer automatischer Versuch." | fehlgeschlagen |
|
||||
| `SKIPPED_ALREADY_PROCESSED` | `≡` | Grau | „Übersprungen – wurde bereits in einem früheren Lauf erfolgreich verarbeitet." | übersprungen |
|
||||
| `SKIPPED_FINAL_FAILURE` | `⊘` | Dunkelgrau | „Endgültig übersprungen nach wiederholten Fehlern." | endgültig übersprungen |
|
||||
| `READY_FOR_AI` | `⟳` | Blau | „Wartet auf Verarbeitung." | – |
|
||||
| `PROPOSAL_READY` | `◇` | Hellblau | „KI-Vorschlag liegt vor, wartet auf Bestätigung." | – |
|
||||
| `PROCESSING` | `▶` | Hellgrau | „Wird gerade verarbeitet." | – |
|
||||
|
||||
**Wichtig:** Farbe ist niemals das einzige Unterscheidungsmerkmal.
|
||||
Icon und Tooltip-Text müssen den Status allein eindeutig beschreiben.
|
||||
|
||||
---
|
||||
|
||||
## UX-Polishing-Features
|
||||
|
||||
### #77 – Fehlende Tooltips
|
||||
|
||||
#### Problem
|
||||
|
||||
Der V3.0-Produkttest hat GUI-Elemente identifiziert, die noch keinen Tooltip
|
||||
tragen. Die Infrastruktur (`GuiTooltipTexts`, `setTooltip()`) existiert bereits
|
||||
aus #66 – es fehlt nur die konsequente Anwendung.
|
||||
|
||||
#### Lösung
|
||||
|
||||
Vor der Implementierung führt Claude Code eine **vollständige Bestandsaufnahme**
|
||||
durch: Alle interaktiven Elemente auf allen Tabs werden gegen vorhandene Tooltips
|
||||
geprüft. Maßgeblich ist die Bestandsaufnahme – die Zahl 16 stammt aus dem
|
||||
Produkttest und ist nicht bindend. Werden mehr fehlende Elemente gefunden,
|
||||
werden alle ergänzt.
|
||||
|
||||
Fehlende Tooltips werden in `GuiTooltipTexts` als Konstanten ergänzt und
|
||||
im jeweiligen GUI-Tab via `element.setTooltip(new Tooltip(GuiTooltipTexts.XY))`
|
||||
gesetzt. Keine hartcodierten Strings.
|
||||
|
||||
**Tooltips auf `TableColumn`-Headern (Sonderfall JavaFX):**
|
||||
|
||||
`TableColumn` ist kein normaler JavaFX-Node; `setTooltip()` ist darauf nicht
|
||||
direkt anwendbar. **Kein Skin-/Lookup-Hack.** Falls Header-Tooltips benötigt
|
||||
werden, wird ein `Label` als Column-Graphic gesetzt:
|
||||
|
||||
```java
|
||||
Label headerLabel = new Label("Spaltenname");
|
||||
headerLabel.setTooltip(new Tooltip("Erklärungstext"));
|
||||
column.setGraphic(headerLabel);
|
||||
column.setText("");
|
||||
```
|
||||
|
||||
Bei der Umsetzung muss geprüft werden, dass Sortierung, Header-Breite
|
||||
und bestehendes CSS durch das Column-Graphic-Pattern nicht sichtbar
|
||||
verschlechtert werden.
|
||||
|
||||
Falls das Projekt bereits eine stabile eigene Lösung für Column-Tooltips
|
||||
besitzt, wird diese wiederverwendet.
|
||||
|
||||
**Zu prüfende Tabs und Elemente (Anhaltspunkte):**
|
||||
|
||||
| Tab | Verdächtige Elemente |
|
||||
|---|---|
|
||||
| Verlauf | Tabellenspalten-Header, Suchfeld, Such-Button, Aktions-Buttons (Reset, Löschen) |
|
||||
| Verlauf (Detail) | Status-Icon, Versuche-Tabelle Spalten, KI-Begründung-Bereich |
|
||||
| Prompt | Speichern-Button, Zurücksetzen-Button, TextArea |
|
||||
| Allgemein | Fortschrittsbalken, Summary-Banner-Elemente |
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui` und `GuiTooltipTexts`.
|
||||
Keine Architektur-Änderungen.
|
||||
|
||||
---
|
||||
|
||||
### #80 – Dirty-Indikator für Konfigurations-Tab
|
||||
|
||||
#### Problem
|
||||
|
||||
Der Prompt-Tab zeigt bereits einen `*`-Dirty-Indikator im Tab-Titel und warnt
|
||||
beim Verlassen mit ungespeicherten Änderungen. Der Konfigurations-Tab hat dieses
|
||||
Verhalten nicht – Nutzer verlieren versehentlich Änderungen.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Dirty-State-Tracking mit Baseline-Snapshot:**
|
||||
|
||||
Beim Laden einer Konfiguration wird ein **Baseline-Snapshot** des geladenen Zustands
|
||||
gespeichert. Dirty-State entsteht durch Vergleich des aktuellen Formularinhalts
|
||||
mit dem Snapshot – nicht durch blindes „erster Listener feuert".
|
||||
|
||||
Während programmgesteuertem Laden oder Normalisieren von Feldinhalten wird
|
||||
Dirty-Tracking temporär unterdrückt (Flag `loadingInProgress`), damit
|
||||
programmatische Feldänderungen keinen unechten Dirty-State auslösen.
|
||||
|
||||
- Beim ersten echten Nutzerwechsel gegenüber dem Snapshot: Tab-Titel wechselt
|
||||
auf `* Konfiguration`
|
||||
- Dirty-Flag wird zurückgesetzt bei: Speichern, Speichern unter,
|
||||
Laden einer neuen Konfiguration (nach Bestätigungsdialog)
|
||||
|
||||
**Bestätigungsdialog bei Navigation mit Dirty State:**
|
||||
|
||||
Beim Laden einer neuen Konfiguration oder beim Schließen der Anwendung
|
||||
mit ungespeicherten Konfig-Änderungen:
|
||||
> „Die Konfiguration enthält ungespeicherte Änderungen. Jetzt speichern?"
|
||||
> [Speichern] [Verwerfen] [Abbrechen]
|
||||
|
||||
**Kopplung mit #87 (Neue Datenbank):**
|
||||
|
||||
Legt der Nutzer über „Neue Datenbank anlegen..." eine neue DB-Datei an,
|
||||
wird der DB-Pfad im Konfigurationsmodell geändert und der Konfig-Tab
|
||||
in den Dirty-State versetzt. Der bestehende Bestätigungsdialog greift
|
||||
beim nächsten Schließen oder Ladevorgang.
|
||||
|
||||
**UX-Konsistenz mit Prompt-Tab:**
|
||||
|
||||
Die UX muss identisch zum Prompt-Tab sein: Sternchen im Tab-Titel,
|
||||
Warn-/Speicherdialog beim Verlassen, Rücksetzen nach Speichern.
|
||||
Die **technische Umsetzung** darf im Konfig-Tab über Baseline-Snapshot
|
||||
und `loadingInProgress` erfolgen, wenn die komplexere Formularlogik
|
||||
das erfordert.
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Kein neuer Port, kein Use-Case.
|
||||
|
||||
---
|
||||
|
||||
### #81 – Enum-Werte statt deutscher Bezeichnungen
|
||||
|
||||
#### Problem
|
||||
|
||||
Die Status-ComboBox im Verlauf-Tab zeigt rohe Enum-Namen (`READY_FOR_AI`,
|
||||
`FAILED_FINAL` etc.). Die Versuche-Tabelle im Detailbereich zeigt ebenfalls
|
||||
Enum-Rohnamen in der Status-Spalte. Das ist für Endnutzer unlesbar.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Anzeige-Mapping:**
|
||||
|
||||
`ProcessingStatusPresentation` (existiert bereits aus #51) stellt die Mapping-Logik
|
||||
bereit. Dieses Mapping wird für alle Statusanzeigen im Verlauf-Tab verbindlich genutzt.
|
||||
**Alle acht Statuswerte der autoritativen Tabelle müssen abgedeckt sein:**
|
||||
|
||||
| Enum-Wert | Angezeigter Text |
|
||||
|---|---|
|
||||
| `SUCCESS` | „✓ Erfolgreich" |
|
||||
| `FAILED_RETRYABLE` | „↻ Temporärer Fehler" |
|
||||
| `FAILED_FINAL` | „× Dauerhaft fehlgeschlagen" |
|
||||
| `SKIPPED_ALREADY_PROCESSED` | „≡ Bereits verarbeitet" |
|
||||
| `SKIPPED_FINAL_FAILURE` | „⊘ Endgültig übersprungen" |
|
||||
| `READY_FOR_AI` | „⟳ Wartet auf Verarbeitung" |
|
||||
| `PROPOSAL_READY` | „◇ Vorschlag vorhanden" |
|
||||
| `PROCESSING` | „▶ In Bearbeitung" |
|
||||
|
||||
**Status-ComboBox:**
|
||||
|
||||
- Erster Eintrag: „Alle Status" – GUI-intern als `Optional.empty()` bzw. `null`-Filter
|
||||
behandelt; kein Domain-Enum-Wert
|
||||
- Weitere Einträge: alle acht Statuswerte mit Displaytext
|
||||
- Intern wird für DB-Queries stets der Enum-Name verwendet
|
||||
- `StringConverter<ProcessingStatus>` implementieren
|
||||
|
||||
**Versuche-Tabelle (Detailbereich):**
|
||||
|
||||
- Status-Spalte: `ProcessingStatusPresentation`-Mapping anwenden
|
||||
- Kein Enum-Rohname darf für Endnutzer sichtbar sein
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Kein neuer Port, kein Use-Case.
|
||||
|
||||
---
|
||||
|
||||
### #83 – KI-Begründung bei SUCCESS-Versuch verwirrend leer
|
||||
|
||||
#### Problem
|
||||
|
||||
Im Detailbereich wird bei einem Versuch mit Status `SUCCESS` die
|
||||
KI-Begründungs-TextArea leer angezeigt. Nutzer verstehen nicht, ob das
|
||||
ein Fehler ist oder ob tatsächlich keine Begründung vorliegt.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Platzhalter über JavaFX `promptText` (kein echter Textinhalt):**
|
||||
|
||||
Bei leerem oder null `ai_reasoning` gilt:
|
||||
|
||||
```java
|
||||
textArea.setText("");
|
||||
textArea.setPromptText("Keine KI-Begründung für diesen Versuch gespeichert.");
|
||||
```
|
||||
|
||||
Der `promptText` wird von JavaFX automatisch gedimmt dargestellt und ist
|
||||
**nicht kopierbar, nicht speicherbar, nicht als Nutzdaten behandelbar**.
|
||||
Kein Vermischen von Daten und UI-Platzhaltertext.
|
||||
|
||||
Die TextArea bleibt sichtbar – ein leeres Feld ohne Erklärung ist schlechter
|
||||
als ein erklärender Platzhalter.
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Kein neuer Port, kein Use-Case,
|
||||
keine DB-Änderung.
|
||||
|
||||
---
|
||||
|
||||
### #84 – Aktionsbuttons nach Laufende nicht sofort reaktiviert
|
||||
|
||||
#### Problem
|
||||
|
||||
Nach Abschluss eines Verarbeitungslaufs bleiben die Aktionsbuttons im Verlauf-Tab
|
||||
(„Status zurücksetzen", „Eintrag löschen") dauerhaft deaktiviert.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Ereignisgetriebene Button-State-Neuberechnung:**
|
||||
|
||||
Der Button-State wird nach jedem Lauf-Terminierungsereignis neu berechnet –
|
||||
unabhängig vom Grund der Terminierung:
|
||||
|
||||
- Erfolgreicher Laufabschluss
|
||||
- Fehlerabbruch (Exception im Worker)
|
||||
- Nutzerabbruch
|
||||
- Leerlauf (keine Dateien zu verarbeiten)
|
||||
|
||||
Nach Terminierung wird, sofern eine Auswahl in der Verlauf-Tabelle besteht,
|
||||
der zugehörige Aktionsbutton-State **ereignisgetrieben** aktiviert –
|
||||
ohne dass der Nutzer die Auswahl erneuern oder den Tab wechseln muss.
|
||||
|
||||
**Code-Analyse erforderlich:** Claude Code analysiert den genauen Signal-Pfad
|
||||
(Laufabschluss-Event → UI-Komponente) und korrigiert die fehlende
|
||||
`Platform.runLater()`-Kopplung.
|
||||
|
||||
**Technisch:** Vermutlich `adapter-in-gui` und ggf. `bootstrap` (Bridge-Verdrahtung).
|
||||
Kein neuer Port, kein Use-Case.
|
||||
|
||||
---
|
||||
|
||||
### #88 – FAILED_FINAL ohne Fehlerursache im Verlauf-Tab
|
||||
|
||||
#### Problem
|
||||
|
||||
Der Detailbereich zeigt bei `FAILED_FINAL`-, `FAILED_RETRYABLE`- und
|
||||
`SKIPPED_FINAL_FAILURE`-Einträgen keine Fehlerursache an.
|
||||
Der Nutzer sieht nur den Status-Icon.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Schema-/Code-Analyse als blockierender erster Schritt:**
|
||||
|
||||
Vor jeder weiteren Implementierung dokumentiert Claude Code verbindlich,
|
||||
welcher Fall vorliegt:
|
||||
|
||||
**Fall A – geeignetes Fehlerfeld bereits vorhanden:**
|
||||
`processing_attempt` enthält bereits ein nutzbares Fehlerfeld.
|
||||
→ Keine Migration. GUI und Abfrage werden um die Anzeige erweitert.
|
||||
|
||||
**Fall B – kein geeignetes Fehlerfeld vorhanden:**
|
||||
→ Flyway-Migration mit der **nächsten freien Versionsnummer** zum Zeitpunkt
|
||||
der Implementierung. Fehlerdetails können nur für ab V3.1 erzeugte
|
||||
Verarbeitungsversuche gespeichert werden. Bestehende Einträge bleiben
|
||||
unverändert und zeigen den Platzhalter „Keine Fehlerdetails gespeichert."
|
||||
|
||||
**Fall C – Fehlerdetails werden bisher nur im Log gespeichert:**
|
||||
→ Migration zwingend erforderlich. Zusätzlich muss der Fehlerpfad der
|
||||
Verarbeitungslogik um Persistierung der Fehlerdetails erweitert werden.
|
||||
|
||||
**Domain-Modul-Einschränkung:**
|
||||
|
||||
`pdf-umbenenner-domain` bleibt unverändert, sofern die benötigten
|
||||
Fehlerdetails ausschließlich über bestehende oder application-nahe
|
||||
History-DTOs transportiert werden können.
|
||||
|
||||
Falls das fachliche Attempt-Modell im Domain-Modul liegt und für die
|
||||
Anzeige erweitert werden muss, ist eine **minimale Domain-Erweiterung zulässig**.
|
||||
Keine Änderung an der fachlichen Kernverarbeitung.
|
||||
|
||||
**Datenmodell (bei Migration – Fall B oder C):**
|
||||
|
||||
```sql
|
||||
-- Versionsnummer = nächste freie Flyway-Version zum Zeitpunkt der Implementierung
|
||||
ALTER TABLE processing_attempt ADD COLUMN failure_details TEXT;
|
||||
```
|
||||
|
||||
`failure_details` enthält eine **nutzerverständliche, gekürzte Fehlerbeschreibung**.
|
||||
Provider- oder Exception-Meldungen werden **nicht roh persistiert** –
|
||||
gespeichert wird eine kontrolliert erzeugte Kurzmeldung aus bekannten
|
||||
Fehlerkategorien oder eine bereinigte/gekürzte Message ohne Stacktrace,
|
||||
API-Keys oder vollständige Provider-Rohantworten.
|
||||
|
||||
Die Begrenzung auf **1000 Zeichen wird spätestens vor Persistierung im
|
||||
DB-Adapter erzwungen**: Längere Texte werden gekürzt und mit „…" markiert.
|
||||
Falls bereits vorher ein zentrales Fehler-Mapping existiert, darf dort
|
||||
gekürzt werden. Entscheidend: in die DB gelangen nur gekürzte, bereinigte
|
||||
Fehlerdetails. Kein SQL-`CHECK`-Constraint (um Alt-/Importdaten nicht
|
||||
zu blockieren).
|
||||
|
||||
**„Letzter Versuch" – Definition:**
|
||||
|
||||
Die angezeigte Fehlerursache stammt aus dem Versuch mit dem höchsten
|
||||
`attempt_number`. Bei Gleichstand wird der mit dem jüngsten `ended_at` verwendet.
|
||||
|
||||
Die Sortierung wird im Rahmen der Code-Analyse gegen das vorhandene Schema
|
||||
verifiziert. Falls `attempt_number` oder `ended_at` nicht existieren, wird
|
||||
die technisch eindeutige Sortierung des Attempt-Verlaufs verwendet und
|
||||
in der Implementierungsnotiz dokumentiert.
|
||||
|
||||
**Anzuzeigende Status:**
|
||||
|
||||
Fehlerursache wird angezeigt bei:
|
||||
- `FAILED_FINAL`
|
||||
- `FAILED_RETRYABLE`
|
||||
- `SKIPPED_FINAL_FAILURE` (zeigt die letzte bekannte Fehlerursache des
|
||||
zugrundeliegenden fehlgeschlagenen Attempts – fachlich konsistent,
|
||||
da `SKIPPED_FINAL_FAILURE` direkte Folge eines endgültigen Fehlschlags ist)
|
||||
|
||||
Bei fehlendem `failure_details` (NULL oder leer): Platzhaltertext via `promptText`
|
||||
analog zu #83.
|
||||
|
||||
**Technisch:** `adapter-in-gui` (Anzeige), ggf. `adapter-out-db`
|
||||
(Abfrage-Erweiterung), ggf. Flyway-Migration, ggf. minimale Domain-Erweiterung.
|
||||
|
||||
---
|
||||
|
||||
### #91 – Lock-File relativer Pfad
|
||||
|
||||
#### Problem
|
||||
|
||||
Der Lock-Mechanismus nutzt einen konfigurierten oder Standard-Pfad für die
|
||||
Lock-Datei. Bei relativem Pfad ist das Verzeichnis abhängig vom aktuellen
|
||||
Arbeitsverzeichnis. Liegt die JAR unter `C:\Program Files`, ist das Verzeichnis
|
||||
zudem nicht beschreibbar.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Verhalten abhängig vom Pfadtyp:**
|
||||
|
||||
**Absolut konfigurierter Pfad:**
|
||||
Wird unverändert verwendet. Schlägt das Anlegen fehl, erfolgt **kein Fallback** –
|
||||
der Nutzer hat den Speicherort explizit vorgegeben. Start bricht mit klarer
|
||||
Fehlermeldung ab.
|
||||
|
||||
**Relativer oder nicht konfigurierter (Default-)Pfad – zweistufige Fallback-Strategie:**
|
||||
|
||||
1. **Primär:** Auflösung relativ zum Verzeichnis der JAR-Datei
|
||||
(`CodeSource.getLocation()`)
|
||||
2. **Fallback:** Auflösung relativ zu `user.home`
|
||||
3. **Abbruch:** Erst wenn auch `user.home` fehlschlägt
|
||||
|
||||
**Parent-Verzeichnisse** werden bei Bedarf automatisch angelegt
|
||||
(`Files.createDirectories()`).
|
||||
|
||||
Der final verwendete **absolute Pfad wird beim Start geloggt** (INFO-Level):
|
||||
```
|
||||
Lock-Datei: C:\Users\Funny\Documents\pdf-umbenenner.lock
|
||||
```
|
||||
|
||||
**Gilt für GUI- und Headless-Start.**
|
||||
|
||||
**Code-Analyse erforderlich:** Claude Code ermittelt die aktuelle
|
||||
Lock-Implementierungslokation (`bootstrap` oder `adapter-out-db`).
|
||||
|
||||
---
|
||||
|
||||
## GUI-Features
|
||||
|
||||
### #82 – Verlauf-Tab: Live-Filter bei Suche
|
||||
|
||||
#### Problem
|
||||
|
||||
Die Suche im Verlauf-Tab wird nur durch expliziten Klick auf den Such-Button
|
||||
ausgelöst. Das erfordert unnötige Interaktion bei jeder Suchanpassung.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Live-Filter mit Debounce und Generation-Counter:**
|
||||
|
||||
- Das Suchfeld erhält einen `ChangeListener` auf die `textProperty()`
|
||||
- Bei jeder Texteingabe startet ein JavaFX-`Timeline`-Debounce-Timer (300 ms)
|
||||
- Nach 300 ms ohne weitere Eingabe wird die DB-Abfrage auf einem Worker-Thread gestartet
|
||||
|
||||
**Race-Condition-Schutz via Generation-Counter:**
|
||||
|
||||
Jede gestartete Suchanfrage erhält eine aufsteigende Generations-ID (atomarer
|
||||
`long`-Counter). Der Worker-Thread trägt seine Generations-ID ins Ergebnis.
|
||||
Beim `Platform.runLater()`-Callback wird das Ergebnis nur in die UI übernommen,
|
||||
wenn die Generations-ID noch aktuell ist – veraltete Worker-Ergebnisse
|
||||
werden verworfen.
|
||||
|
||||
**Such-Button und Enter-Taste:**
|
||||
|
||||
- Klick auf Such-Button oder Enter im Suchfeld: Debounce-Timer sofort abgebrochen,
|
||||
Suche unverzüglich gestartet
|
||||
- Barrierefreiheit: Such-Button bleibt erhalten
|
||||
|
||||
**Auswahlverhalten nach neuen Suchergebnissen:**
|
||||
|
||||
Nach jeder Übernahme neuer Suchergebnisse wird die Tabellenauswahl
|
||||
**vollständig geleert**. Detailbereich und Aktionsbuttons werden entsprechend
|
||||
zurückgesetzt. Das ist robuster als ein Abgleich der alten Auswahl gegen
|
||||
die neue Ergebnisliste und vermeidet Wechselwirkungen mit #86.
|
||||
|
||||
**Leeres Suchfeld:** Zeigt alle Einträge (bis LIMIT 501).
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Die bestehende Suchabfrage via
|
||||
`GuiHistoryOverviewPort` wird unverändert wiederverwendet.
|
||||
|
||||
---
|
||||
|
||||
### #86 – Mehrfachauswahl im Verlauf-Tab
|
||||
|
||||
#### Problem
|
||||
|
||||
Der Verlauf-Tab erlaubt nur Einzelauswahl. Bulk-Operationen sind nicht möglich.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Multi-Select-Modus:**
|
||||
|
||||
```java
|
||||
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
```
|
||||
|
||||
JavaFX stellt damit Strg+Klick und Shift+Klick automatisch bereit.
|
||||
|
||||
**Strg+A – Fokusabhängig:**
|
||||
|
||||
Strg+A selektiert alle sichtbaren Tabelleneinträge **nur, wenn die Verlauf-Tabelle
|
||||
den Fokus besitzt**. Liegt der Fokus im Suchfeld, bleibt Strg+A die normale
|
||||
Textauswahl im Suchfeld.
|
||||
|
||||
**Detailbereich bei Mehrfachauswahl:**
|
||||
|
||||
- Genau 1 Eintrag: Detailbereich wie bisher
|
||||
- Mehrere Einträge: Platzhaltertext „X Einträge ausgewählt."
|
||||
|
||||
**Snapshot der fachlichen Schlüssel vor Worker-Thread-Start:**
|
||||
|
||||
Vor dem Start einer Bulk-Operation wird ein **unveränderlicher Snapshot der
|
||||
fachlichen Schlüssel** erstellt, die die bestehenden Reset-/Delete-Use-Cases
|
||||
erwarten (typischerweise Fingerprints, sofern das die vorhandene Use-Case-Signatur
|
||||
erwartet). Der Worker-Thread arbeitet ausschließlich auf diesem Snapshot –
|
||||
nie auf einer Live-`ObservableList`, die sich während der Operation ändern könnte.
|
||||
|
||||
**Aktionsbuttons bei Mehrfachauswahl:**
|
||||
|
||||
| Aktion | Verhalten |
|
||||
|---|---|
|
||||
| „Status zurücksetzen" | Aktiv bei ≥ 1 Auswahl; Bestätigungsdialog: „X Einträge zurücksetzen?" |
|
||||
| „Eintrag löschen" | Aktiv bei ≥ 1 Auswahl; Bestätigungsdialog: „X Einträge unwiderruflich löschen?" |
|
||||
|
||||
**Bulk-Fehlerstrategie (Partial Success):**
|
||||
|
||||
Schlägt eine Operation bei einzelnen Einträgen fehl, werden die restlichen
|
||||
trotzdem abgearbeitet. Nach Abschluss erscheint ein **kompakter**
|
||||
Zusammenfassungsdialog:
|
||||
> „X von Y Einträgen erfolgreich verarbeitet. Z Einträge konnten nicht
|
||||
> verarbeitet werden."
|
||||
|
||||
Detaillierte Einzelfehler werden geloggt, nicht in den Dialog gestopft.
|
||||
|
||||
**Ausführung:** Bulk-Operationen rufen die bestehenden Use-Cases
|
||||
(`DefaultResetDocumentStatusUseCase`, `DefaultDeleteDocumentHistoryUseCase`)
|
||||
sequenziell auf dem Worker-Thread auf. Keine neuen Use-Cases erforderlich.
|
||||
|
||||
**Sperren während Lauf:** Alle Aktions-Buttons deaktiviert während eines
|
||||
aktiven Verarbeitungslaufs.
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Keine neuen Ports oder Use-Cases.
|
||||
|
||||
---
|
||||
|
||||
### #87 – Neue leere SQLite-Datenbank anlegen
|
||||
|
||||
#### Problem
|
||||
|
||||
Will der Nutzer mit einer frischen Datenbank starten, muss er die Datei
|
||||
manuell löschen. Das ist umständlich und fehleranfällig.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Neuer Menüpunkt:**
|
||||
|
||||
`Datenbank → Neue Datenbank anlegen...`
|
||||
|
||||
(Nur aktiv wenn kein Verarbeitungslauf läuft.)
|
||||
|
||||
**Eigentümer des aktiven Datenbankkontexts:**
|
||||
|
||||
Der Runtime-Wechsel der aktiven Datenbank erfordert eine zentrale Komponente,
|
||||
die den aktiven Datenbankkontext besitzt. Vor der Implementierung analysiert
|
||||
Claude Code, ob eine solche Komponente bereits existiert.
|
||||
|
||||
- **Fall A – wechselbarer DB-Kontext vorhanden:** Vorhandene Komponente
|
||||
wird genutzt/erweitert.
|
||||
- **Fall B – kein wechselbarer DB-Kontext vorhanden:** Es wird ein minimaler
|
||||
`ActiveDatabaseContextPort` eingeführt (Outbound-Port in `application`,
|
||||
Adapter in `bootstrap` oder `adapter-out-db`). Dieser Port ist die einzige
|
||||
Stelle, an der die aktive DB-Referenz umgestellt wird.
|
||||
|
||||
**Der DB-Wechsel darf nicht im JavaFX-Code versteckt werden.**
|
||||
Der Use-Case `DefaultCreateNewDatabaseUseCase` orchestriert den Wechsel;
|
||||
die physische Umstellung der Verbindung delegiert er über den Port.
|
||||
|
||||
**Ablauf (atomar aus Anwendungssicht):**
|
||||
|
||||
1. `FileChooser` öffnet (Filter: `*.sqlite`); Nutzer wählt Zieldatei
|
||||
2. **Pfad-Sicherheitsprüfung:**
|
||||
Die aktive DB und die gewählte Zieldatei werden über **normalisierte,
|
||||
absolut aufgelöste Pfade** verglichen – kein Rohstring-Vergleich.
|
||||
Für existierende Dateien wird `toRealPath()` verwendet; für noch nicht
|
||||
existierende Dateien wird der Parent-Pfad real aufgelöst und der Dateiname
|
||||
normalisiert verglichen. Unter Windows erfolgt der Vergleich case-insensitive.
|
||||
Bei Übereinstimmung: klare Fehlermeldung, kein Überschreiben.
|
||||
3. Existiert die Zieldatei (andere als aktive DB): Bestätigungsdialog
|
||||
„Die Datei existiert bereits. Überschreiben?"
|
||||
4. **GUI-Sperre:** Während Anlage und Wechsel befindet sich die GUI in einem
|
||||
`DB-Busy`-Zustand. Alle DB-lesenden und DB-schreibenden Aktionen
|
||||
(Live-Suche, Bulk-Reset, Bulk-Delete, Verlauf-Refresh, erneuter
|
||||
Klick auf „Neue Datenbank anlegen") sind deaktiviert. Der Zustand
|
||||
wird nach Erfolg oder Fehler zuverlässig zurückgesetzt.
|
||||
5. Neue SQLite-Datei wird als **temporäre Datei im Zielverzeichnis** erzeugt
|
||||
6. Flyway führt alle verfügbaren Migrationsskripte gegen die temporäre Datei aus
|
||||
(`migrate()` auf neuesten Schema-Stand)
|
||||
7. Neue DB-Verbindung wird **testweise geöffnet und geprüft** (gegen Temp-Datei).
|
||||
Der Verbindungstest prüft mindestens:
|
||||
- SQLite-Verbindung kann geöffnet werden
|
||||
- Flyway-Schema-History ist vorhanden
|
||||
- Eine einfache Leseabfrage gegen Schema-Metadaten ist erfolgreich
|
||||
8. Erst nach erfolgreichem Test: temporäre Datei zur Zieldatei verschoben.
|
||||
Bei bereits existierender, bestätigter Zieldatei wird
|
||||
`Files.move(tempFile, targetFile, ATOMIC_MOVE, REPLACE_EXISTING)` verwendet,
|
||||
sofern vom Dateisystem unterstützt. Die vorhandene Zieldatei wird vorher
|
||||
**nicht separat gelöscht**. Wird die Kombination `ATOMIC_MOVE + REPLACE_EXISTING`
|
||||
nicht unterstützt, bricht der Vorgang mit klarer Fehlermeldung ab –
|
||||
kein unsicherer halb-atomarer Fallback.
|
||||
9. Aktive DB-Referenz der Anwendung umgestellt (via `ActiveDatabaseContextPort`)
|
||||
10. Verlauf-Tab neu geladen → zeigt „Noch keine Verarbeitungen vorhanden."
|
||||
11. Statuszeile aktualisiert DB-Pfad
|
||||
12. DB-Pfad im Konfigurationsmodell geändert → Konfig-Tab wechselt in Dirty-State
|
||||
13. Statuszeile oder Meldungsbereich zeigt:
|
||||
„Neue Datenbank ist aktiv. Konfiguration speichern, damit diese DB
|
||||
beim nächsten Start verwendet wird."
|
||||
|
||||
**Fehlerfall ohne partielle Änderung:**
|
||||
|
||||
Schlägt ein Schritt (Anlegen, Flyway, Verbindungstest, Move) fehl, bleibt die
|
||||
bisher aktive DB **vollständig unverändert in Betrieb**. Die temporäre Datei
|
||||
wird gelöscht. Fehlerdialog mit konkreter Meldung.
|
||||
|
||||
**Headless:** Die Funktion ist ausschließlich GUI-seitig aufrufbar.
|
||||
`adapter-in-cli` ist nicht betroffen.
|
||||
|
||||
**Architektur:**
|
||||
|
||||
| Komponente | Typ | Modul | Zweck |
|
||||
|---|---|---|---|
|
||||
| `CreateNewDatabaseUseCase` | Inbound-Port-Interface | `application` | Vertrag: `createNewDatabase(Path)` |
|
||||
| `DefaultCreateNewDatabaseUseCase` | Use-Case-Impl. | `application` | Atomarer DB-Wechsel: Temp-Datei, Flyway, Test, Move, Kontext-Umstellung |
|
||||
| `DatabaseCreationPort` | Outbound-Port | `application` | `createAndInitialize(Path tempFile)` |
|
||||
| `ActiveDatabaseContextPort` | Outbound-Port | `application` | `switchActiveDatabase(Path newDbFile)` – Eigentümer des Laufzeitkontexts |
|
||||
| `GuiCreateNewDatabasePort` | Bridge-Interface | `adapter-in-gui` | Brücke zum Use-Case |
|
||||
| `SqliteDatabaseCreationAdapter` | Outbound-Adapter | `adapter-out-db` | SQLite-Temp-Datei erzeugen, Flyway migrate auf latest, Verbindung testen |
|
||||
| `SqliteActiveDatabaseContextAdapter` | Outbound-Adapter | `bootstrap` oder `adapter-out-db` | Umschalten der aktiven DB-Referenz (Analyse erforderlich) |
|
||||
|
||||
---
|
||||
|
||||
### #32 – Mausrad-Zoom in PDF-Vorschau
|
||||
|
||||
#### Problem
|
||||
|
||||
Die PDF-Vorschau lässt sich nur über die Zoom-Buttons skalieren.
|
||||
Ein Mausrad-Zoom fehlt.
|
||||
|
||||
#### Lösung
|
||||
|
||||
**Scroll-Event auf der PDF-Vorschau-Komponente:**
|
||||
|
||||
```java
|
||||
scrollPane.addEventFilter(ScrollEvent.SCROLL, event -> {
|
||||
if (event.isControlDown()) {
|
||||
accumulateAndApplyZoomDelta(event.getDeltaY());
|
||||
event.consume(); // immer konsumieren bei Strg, kein paralleles Scrollen
|
||||
}
|
||||
// ohne Strg: normales Scrollen bleibt
|
||||
});
|
||||
```
|
||||
|
||||
**Bei gedrückter Strg-Taste werden ScrollEvents grundsätzlich konsumiert**,
|
||||
damit kein paralleles Scrollen im ScrollPane erfolgt – auch wenn der Delta
|
||||
zu klein für einen Zoomschritt ist.
|
||||
|
||||
**Delta-Akkumulation für Trackpad-Kompatibilität:**
|
||||
|
||||
Sehr kleine Trackpad-Deltas werden **intern akkumuliert**, bis die Mindestschwelle
|
||||
für einen Zoomschritt erreicht ist. Kein Verwerfen: akkumulierte Deltas
|
||||
ergeben bei genug Trackpad-Wischbewegung sauber einen Zoomschritt.
|
||||
Als Orientierungswert gilt ±10 % je „Notch" eines Standard-Mausrads.
|
||||
|
||||
**Zoom-Verhalten:**
|
||||
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| Auslöser | Strg + Mausrad |
|
||||
| Schrittweite | Vorzeichenbasiert auf akkumuliertem `deltaY`, ca. 10 % je Notch |
|
||||
| Minimum | 10 % |
|
||||
| Maximum | 500 % |
|
||||
| Zurücksetzen bei neuem PDF | Ja (Zoom auf Fit-to-Width) |
|
||||
|
||||
**Fit-to-Width-Modus:**
|
||||
|
||||
Nach manuellem Strg+Mausrad-Zoom verlässt die Vorschau den Fit-to-Width-Modus.
|
||||
Fit-to-Width wird erst wieder aktiv, wenn ein neues PDF geladen oder der
|
||||
Fit-to-Width-Button explizit erneut betätigt wird.
|
||||
|
||||
**Viewport-Stabilität:**
|
||||
|
||||
Beim Zoom bleibt die sichtbare Viewport-Mitte möglichst erhalten.
|
||||
|
||||
**Zoom-State-Konsistenz:**
|
||||
|
||||
Der Zoom-State wird über dieselbe Variable geführt, die auch die
|
||||
Toolbar-Zoom-Buttons bedienen.
|
||||
|
||||
**Technisch:** Ausschließlich `adapter-in-gui`. Kein neuer Port, kein Use-Case.
|
||||
|
||||
---
|
||||
|
||||
## Architektur-Zusammenfassung
|
||||
|
||||
### Neue Inbound-Port-Interfaces und Use-Cases
|
||||
|
||||
| Komponente | Typ | Modul | Zweck | Issue |
|
||||
|---|---|---|---|---|
|
||||
| `CreateNewDatabaseUseCase` | Inbound-Port-Interface | `application` | Vertrag für DB-Anlage | #87 |
|
||||
| `DefaultCreateNewDatabaseUseCase` | Use-Case-Impl. | `application` | Atomarer DB-Wechsel via Temp-Datei + Port-Delegation | #87 |
|
||||
|
||||
### Neue Outbound-Ports
|
||||
|
||||
| Komponente | Modul | Zweck | Issue |
|
||||
|---|---|---|---|
|
||||
| `DatabaseCreationPort` | `application` | Temp-Datei erzeugen, Flyway, Verbindungstest | #87 |
|
||||
| `ActiveDatabaseContextPort` | `application` | `switchActiveDatabase(Path)` – Laufzeit-DB-Kontext | #87 |
|
||||
|
||||
### Neue Bridge-Interfaces (adapter-in-gui)
|
||||
|
||||
| Interface | Zweck | Issue |
|
||||
|---|---|---|
|
||||
| `GuiCreateNewDatabasePort` | Brücke zur DB-Anlage | #87 |
|
||||
|
||||
### Neue Adapter
|
||||
|
||||
| Adapter | Modul | Zweck | Issue |
|
||||
|---|---|---|---|
|
||||
| `SqliteDatabaseCreationAdapter` | `adapter-out-db` | SQLite-Temp-Datei, Flyway migrate auf latest, Test | #87 |
|
||||
| `SqliteActiveDatabaseContextAdapter` | `bootstrap` oder `adapter-out-db` | Umschalten der aktiven DB-Referenz (Lokation via Code-Analyse) | #87 |
|
||||
|
||||
### Geänderte Komponenten (adapter-in-gui)
|
||||
|
||||
| Komponente | Änderung | Issues |
|
||||
|---|---|---|
|
||||
| `GuiHistoryTab` | Multi-Select + Schlüssel-Snapshot, Live-Filter + Generation-Counter + Auswahl leeren, Fehlerursache, Platzhalter via promptText, Tooltips, DB-Busy-Sperre | #82, #83, #86, #88, #77, #87 |
|
||||
| `GuiConfigTab` | Dirty-State mit Baseline-Snapshot + loadingInProgress, Tab-Titel, Dialog, Kopplung mit #87 | #80 |
|
||||
| `GuiTooltipTexts` | Neue Tooltip-Konstanten; TableColumn-Header via Column-Graphic-Pattern | #77 |
|
||||
| Verlauf-Detailbereich | Enum-Displaytext (alle 8 Werte), Fehlerursache für FAILED/SKIPPED_FINAL | #81, #88 |
|
||||
| Status-ComboBox | `StringConverter<ProcessingStatus>`, „Alle Status" als GUI-interner Null-Filter | #81 |
|
||||
| PDF-Vorschau-Komponente | Delta-Akkumulation, Strg+Scroll konsumiert, Viewport-Stabilität, Fit-to-Width-Modus | #32 |
|
||||
| Lauf-Abschluss-Signalkette | Ereignisgetriebene Button-State-Neuberechnung für alle Terminierungsgründe | #84 |
|
||||
|
||||
### Geänderte Komponenten (sonstige)
|
||||
|
||||
| Komponente | Modul | Änderung | Issue |
|
||||
|---|---|---|---|
|
||||
| Lock-File-Auflösung | `bootstrap` oder `adapter-out-db` | Absolut: direkt + Abbruch; Relativ: JAR-Dir → user.home → Abbruch; Parent-Dirs; Logging | #91 |
|
||||
|
||||
### Nicht geändert
|
||||
|
||||
- `pdf-umbenenner-domain` – keine Änderungen, außer ggf. minimale Erweiterung
|
||||
für #88 falls Attempt-Modell dort liegt (zulässig, keine Kernverarbeitungslogik)
|
||||
- `pdf-umbenenner-adapter-in-cli` – keine neuen Funktionen
|
||||
- Headless-Verarbeitungslogik – vollständig unberührt
|
||||
- Kernverarbeitungslogik (PDF lesen → KI → umbenennen)
|
||||
|
||||
---
|
||||
|
||||
## Datenbankmigrationen
|
||||
|
||||
Flyway ist die einzige Schema-Evolutionsquelle.
|
||||
|
||||
### Potenzielles Migrationsskript (abhängig von Code-Analyse #88)
|
||||
|
||||
Vor der Implementierung von #88 dokumentiert Claude Code verbindlich,
|
||||
ob ein Fehlerfeld bereits im Schema existiert (Fall A / B / C – siehe #88).
|
||||
|
||||
**Nur bei Fall B oder C:**
|
||||
|
||||
```sql
|
||||
-- Fehlerdetails in processing_attempt ergänzen
|
||||
-- Versionsnummer = nächste freie Flyway-Version zum Zeitpunkt der Implementierung
|
||||
ALTER TABLE processing_attempt ADD COLUMN failure_details TEXT;
|
||||
```
|
||||
|
||||
- `failure_details`: nutzerverständliche, gekürzte Fehlerbeschreibung;
|
||||
Begrenzung auf 1000 Zeichen **vor Persistierung im Adapter** erzwungen,
|
||||
Kürzung mit „…"; kein SQL-`CHECK`-Constraint
|
||||
- Bestehende Zeilen erhalten automatisch `NULL` – kein Datenverlust
|
||||
- Alte Einträge ohne Fehlerdetails zeigen `promptText`-Platzhalter in der GUI
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done (V3.1 gesamt)
|
||||
|
||||
- [ ] Alle 11 Issues implementiert und einzeln getestet
|
||||
- [ ] `mvn clean verify` grün (alle Module, kein `-DskipTests`)
|
||||
- [ ] `mvn clean install -Drevision=3.1.0` – Build ohne Fehler
|
||||
- [ ] Manueller GUI-Produkttest durchgeführt (Green build ≠ fertige Software)
|
||||
- [ ] Keine Enum-Rohnamen in der GUI sichtbar (alle 8 Statuswerte mit Displaytext)
|
||||
- [ ] Alle fehlenden Tooltips vorhanden; TableColumn-Header via Column-Graphic-Pattern
|
||||
- [ ] Dirty-Indikator Konfig-Tab: kein programmgesteuertes Feuern, Baseline-Snapshot korrekt
|
||||
- [ ] Live-Filter: 300 ms Debounce, Generation-Counter, Auswahl nach Suche geleert
|
||||
- [ ] Mehrfachauswahl: Strg+A nur bei Tabellenfokus; Schlüssel-Snapshot; Partial-Success-Dialog
|
||||
- [ ] `FAILED_FINAL`/`FAILED_RETRYABLE`/`SKIPPED_FINAL_FAILURE`: Fehlerursache sichtbar (oder Platzhalter)
|
||||
- [ ] Leere `ai_reasoning`: `promptText`-Platzhalter (kein echter Text)
|
||||
- [ ] Aktionsbuttons ereignisgetrieben reaktiviert nach allen Terminierungsgründen
|
||||
- [ ] #87 Code-Analyse: DB-Kontext-Eigentümer dokumentiert (Fall A oder B)
|
||||
- [ ] #87: Atomarer Ablauf via Temp-Datei; Pfadvergleich normalisiert + case-insensitive
|
||||
- [ ] #87: Aktive DB bleibt bei Fehler unverändert; DB-Busy-Sperre korrekt zurückgesetzt
|
||||
- [ ] #87: Flyway auf neuesten Stand; Hinweismeldung nach Wechsel
|
||||
- [ ] Strg+Mausrad-Zoom: Delta-Akkumulation, immer konsumiert bei Strg, 10%–500%
|
||||
- [ ] Lock-File: Absolut direkt; Relativ zweistufig; Parent-Dirs; Pfad geloggt
|
||||
- [ ] Code-Kommentare auf Deutsch; Logging auf Deutsch
|
||||
- [ ] JavaDoc auf allen neuen öffentlichen Ports, Use-Cases und Adapter-Methoden
|
||||
- [ ] `betrieb.md` und `gui-bedienanleitung.md` auf V3.1-Stand gebracht
|
||||
- [ ] Freigabedokument `freigabe-v3_1.md` erstellt
|
||||
|
||||
---
|
||||
|
||||
## Abnahmekriterien je Feature
|
||||
|
||||
### #77 Fehlende Tooltips
|
||||
- [ ] Vollständige Bestandsaufnahme: Liste aller Elemente ohne Tooltip erstellt
|
||||
- [ ] Alle identifizierten Elemente haben Tooltips (Anzahl aus Bestandsaufnahme)
|
||||
- [ ] TableColumn-Header: Column-Graphic mit Label+Tooltip, kein Skin-/Lookup-Hack
|
||||
- [ ] Column-Graphic: Sortierung, Header-Breite und CSS nicht sichtbar verschlechtert
|
||||
- [ ] Neue Konstanten ausschließlich in `GuiTooltipTexts`, keine hartcodierten Strings
|
||||
|
||||
### #80 Dirty-Indikator Konfig-Tab
|
||||
- [ ] Tab-Titel `* Konfiguration` nur nach echter Nutzeränderung gegenüber Baseline-Snapshot
|
||||
- [ ] Programmgesteuertes Laden setzt kein Dirty-Flag (`loadingInProgress`-Schutz)
|
||||
- [ ] Tab-Titel `Konfiguration` nach Speichern
|
||||
- [ ] Bestätigungsdialog bei Laden neuer Konfig mit Dirty State
|
||||
- [ ] DB-Pfad-Wechsel via #87 setzt Konfig-Tab dirty
|
||||
- [ ] UX identisch zum Prompt-Tab (Sternchen, Dialog, Reset)
|
||||
|
||||
### #81 Enum-Bezeichnungen
|
||||
- [ ] Status-ComboBox: „Alle Status" als erster Eintrag (GUI-interner Null-Filter)
|
||||
- [ ] Status-ComboBox: alle 8 Statuswerte als Displaytext
|
||||
- [ ] Versuche-Tabelle: alle 8 Statuswerte als Displaytext
|
||||
- [ ] DB-Queries intern weiterhin mit Enum-Namen
|
||||
- [ ] Kein Enum-Rohname für Endnutzer sichtbar
|
||||
|
||||
### #82 Live-Filter
|
||||
- [ ] Suche startet nach 300 ms Tipp-Pause automatisch
|
||||
- [ ] Generation-Counter: veraltete Worker-Ergebnisse werden verworfen
|
||||
- [ ] Such-Button / Enter: sofortige Suche, Debounce abgebrochen
|
||||
- [ ] Auswahl nach neuen Suchergebnissen vollständig geleert
|
||||
- [ ] Leeres Suchfeld zeigt alle Einträge
|
||||
- [ ] Worker-Thread, UI via `Platform.runLater()`
|
||||
|
||||
### #83 KI-Begründung leer
|
||||
- [ ] `textArea.setPromptText(...)` bei leerem/null `ai_reasoning`
|
||||
- [ ] `textArea.setText("")` – kein Platzhaltertext als echter Inhalt
|
||||
- [ ] TextArea bleibt sichtbar
|
||||
|
||||
### #84 Buttons reaktivieren
|
||||
- [ ] Aktionsbuttons während Lauf deaktiviert
|
||||
- [ ] Reaktivierung ereignisgetrieben nach: Erfolg, Fehlerabbruch, Nutzerabbruch, Exception
|
||||
- [ ] Keine manuellen Workarounds notwendig
|
||||
|
||||
### #86 Mehrfachauswahl
|
||||
- [ ] `SelectionMode.MULTIPLE` aktiv
|
||||
- [ ] Strg+A nur bei Tabellenfokus (kein Konflikt mit Suchfeld)
|
||||
- [ ] Strg+Klick, Shift+Klick korrekt
|
||||
- [ ] Detailbereich: „X Einträge ausgewählt." bei Mehrfachauswahl
|
||||
- [ ] Schlüssel-Snapshot vor Worker-Thread-Start
|
||||
- [ ] Bulk-Reset: Bestätigungsdialog + Partial-Success-Dialog
|
||||
- [ ] Bulk-Delete: Bestätigungsdialog + Partial-Success-Dialog
|
||||
- [ ] Aktionen während Lauf gesperrt
|
||||
|
||||
### #87 Neue Datenbank anlegen
|
||||
- [ ] Code-Analyse: DB-Kontext-Eigentümer dokumentiert, Fall A oder B entschieden
|
||||
- [ ] Menüpunkt vorhanden, nur außerhalb von Läufen aktiv
|
||||
- [ ] Aktive DB über normalisierten Pfadvergleich (case-insensitive, toRealPath) erkannt
|
||||
- [ ] Bestehende Fremddatei: Überschreiben-Bestätigung
|
||||
- [ ] DB-Busy-Sperre während Anlage aktiv; nach Erfolg/Fehler zuverlässig zurückgesetzt
|
||||
- [ ] Neue DB als Temp-Datei; Flyway auf neuesten Stand
|
||||
- [ ] Verbindungstest: Verbindung öffnen, Flyway-History prüfen, Leseabfrage erfolgreich
|
||||
- [ ] Move mit `ATOMIC_MOVE + REPLACE_EXISTING`; vorhandene Datei nicht vorher separat löschen
|
||||
- [ ] Kein halb-atomarer Fallback bei nicht unterstützter Kombination
|
||||
- [ ] Fehlerfall: Temp-Datei gelöscht, aktive DB unverändert, Fehlerdialog
|
||||
- [ ] `ActiveDatabaseContextPort.switchActiveDatabase()` schaltet Referenz um
|
||||
- [ ] Verlauf-Tab: „Noch keine Verarbeitungen vorhanden."
|
||||
- [ ] Statuszeile aktualisiert DB-Pfad
|
||||
- [ ] Konfig-Tab wechselt in Dirty-State
|
||||
- [ ] Hinweismeldung: Konfiguration speichern nicht vergessen
|
||||
|
||||
### #88 Fehlerursache FAILED_FINAL
|
||||
- [ ] Schema-/Code-Analyse: Fall A/B/C dokumentiert vor Implementierung
|
||||
- [ ] Ggf. Flyway-Migration mit nächster freier Versionsnummer
|
||||
- [ ] Sortierung für „letzter Versuch" gegen Schema verifiziert
|
||||
- [ ] Detailbereich: `failure_details` bei `FAILED_FINAL`, `FAILED_RETRYABLE`, `SKIPPED_FINAL_FAILURE`
|
||||
- [ ] NULL/leer: `promptText`-Platzhalter
|
||||
- [ ] 1000-Zeichen-Grenze spätestens vor DB-Persistierung erzwungen, Kürzung mit „…"
|
||||
- [ ] Keine rohen Provider-/Exception-Meldungen persistiert
|
||||
|
||||
### #91 Lock-File Pfad
|
||||
- [ ] Absoluter Pfad: direkt verwendet, kein Fallback, Abbruch bei Fehler
|
||||
- [ ] Relativer Pfad: erst JAR-Verzeichnis, dann `user.home`, dann Abbruch
|
||||
- [ ] Parent-Verzeichnisse automatisch angelegt
|
||||
- [ ] Absoluter Pfad beim Start geloggt (INFO)
|
||||
- [ ] Gilt für GUI- und Headless-Start
|
||||
|
||||
### #32 Mausrad-Zoom
|
||||
- [ ] Strg+Scroll: Event grundsätzlich konsumiert (kein paralleles Scrollen)
|
||||
- [ ] Delta-Akkumulation für kleine Trackpad-Deltas
|
||||
- [ ] Zoom 10%–500%, ca. 10 % je Notch
|
||||
- [ ] Ohne Strg: normales Scrollen
|
||||
- [ ] Viewport-Mitte beim Zoom möglichst stabil
|
||||
- [ ] Fit-to-Width-Modus verlassen nach manuellem Zoom
|
||||
- [ ] Zoom-Reset bei neuem PDF (Fit-to-Width)
|
||||
- [ ] Zoom-State konsistent mit Toolbar-Zoom-Buttons
|
||||
Reference in New Issue
Block a user