docs: Spec V3.3 nach Code-Reads finalisiert (#74 #98 #99)

This commit is contained in:
2026-05-09 08:36:32 +02:00
parent 40e308f670
commit b63dcf5efa
+30 -20
View File
@@ -1,6 +1,6 @@
# V3.3 Token- und Kosten-Tracking
**Status:** Implementierungsvorbereitend Code-Reads vor Implementierungsstart erforderlich (siehe Schlussabschnitt)
**Status:** Implementierungsbereit alle Code-Reads erledigt (siehe Schlussabschnitt)
**Erstellt:** 2026-05-08
**Aktualisiert:** 2026-05-08 (V6 nach fünftem Review)
**Autor:** Marcus (mit Claude als Mentor)
@@ -250,7 +250,7 @@ also auch Aggregationsschlüssel:
### V3.3-Provider-Whitelist
V3.3 unterstützt fachlich **nur** `openai-compatible` und `anthropic`.
V3.3 unterstützt fachlich **nur** `openai-compatible` und `claude`.
Die DB ist bewusst offen (keine CHECK-Constraint auf `provider`).
Falls in `model_price` ein unbekannter Provider-Wert vorhanden ist,
zeigt die GUI ihn **read-only** mit Hinweis „Unbekannter Provider" an.
@@ -408,9 +408,9 @@ VALUES
('openai-compatible', 'gpt-4.1-nano', 100, 400, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-5', 1250, 10000, 'USD', '2026-05-08T00:00:00Z'),
('openai-compatible', 'gpt-5-mini', 250, 2000, 'USD', '2026-05-08T00:00:00Z'),
('anthropic', 'claude-haiku-4-5-20251001', 1000, 5000, 'USD', '2026-05-08T00:00:00Z'),
('anthropic', 'claude-sonnet-4-6', 3000, 15000, 'USD', '2026-05-08T00:00:00Z'),
('anthropic', 'claude-opus-4-7', 5000, 25000, 'USD', '2026-05-08T00:00:00Z')
('claude', 'claude-haiku-4-5-20251001', 1000, 5000, 'USD', '2026-05-08T00:00:00Z'),
('claude', 'claude-sonnet-4-6', 3000, 15000, 'USD', '2026-05-08T00:00:00Z'),
('claude', 'claude-opus-4-7', 5000, 25000, 'USD', '2026-05-08T00:00:00Z')
ON CONFLICT (provider, model_name) DO NOTHING;
```
@@ -951,6 +951,13 @@ performanter; V3.x falls Performance-Probleme in der Praxis auftreten.
| RunLock-Mechanismus | Bleibt unverändert |
| SQL-Boolean-Resultsets | Adapter mappt via `getInt(...) != 0` |
**Implementierungshinweis:** Code-Read hat ergeben, dass mehrere Repository-Adapter direkte
`DriverManager.getConnection`-Aufrufe ohne `SQLiteConfig` verwenden
(`SqliteProcessingAttemptRepositoryAdapter`, `SqliteUnitOfWorkAdapter` u.a.). Eine zentrale
DataSource würde diese nicht abdecken. AP-A umfasst daher zwingend die Einführung einer
zentralen Connection-Factory sowie den Umzug aller direkten `DriverManager.getConnection`-Stellen
auf diese Factory, bevor WAL und `busy_timeout` wirksam sind.
### Hook im `BatchRunProcessingUseCase`
```
@@ -986,9 +993,9 @@ performanter; V3.x falls Performance-Probleme in der Praxis auftreten.
- **Tabelle** in `$ pro 1M Tokens`; intern Nano-USD/Token. Eingabe max 6 Nachkommastellen.
- **Provider und Modellname** read-only nach Speichern (Composite Primary Key)
- **Unbekannte Provider** (nicht `openai-compatible`/`anthropic`): Zeile **read-only mit Tooltip-Icon** `ⓘ` „Unbekannter Provider Bearbeitung in V3.3 nicht unterstützt"; **Löschen ist erlaubt**
- **Unbekannte Provider** (nicht `openai-compatible`/`claude`): Zeile **read-only mit Tooltip-Icon** `ⓘ` „Unbekannter Provider Bearbeitung in V3.3 nicht unterstützt"; **Löschen ist erlaubt**
- **Lösch-Button** mit Bestätigungsdialog
- **„Modell hinzufügen"**-Dialog: Provider-Combobox nur `openai-compatible` und `anthropic`
- **„Modell hinzufügen"**-Dialog: Provider-Combobox nur `openai-compatible` und `claude`
- **„Speichern"**: transaktionaler Batch
- **`updatedAt = null`** → Spalte zeigt „ungültig" mit Tooltip
@@ -1063,9 +1070,13 @@ Pro-Attempt-Snapshot. Identischer Modellname zwei Provider → zwei Zeilen.
#### Quellname / Neuster Name
`last_known_source_file_name` (LEFT JOIN, NULL→„–"); `final_target_file_name`
`last_known_source_file_name` (mutabel wird bei jedem Verarbeitungslauf auf
den aktuellen Quellpfad aktualisiert; zeigt daher den zuletzt bekannten Quellnamen,
nicht zwingend den ursprünglichen) (LEFT JOIN, NULL→„–"); `final_target_file_name`
des jüngsten Versuchs (NULL→„–").
Tooltip Quellname-Spalte: „letzter bekannter Quellname (wird bei Wiederholung aktualisiert)".
#### Banner und Gesamt-Kosten-Kopfzeile
Gemäß Anzeige-Semantik. Kopfzeile bezieht sich auf vollständigen Zeitraum.
@@ -1210,7 +1221,7 @@ Anthropic-Modell-IDs mit achtstelligem Datumssuffix ohne Trennstriche
| Flyway-Migration | Läuft **vor** jedem CLI-DB-Zugriff (Schema-Aktualität sicherstellen) |
| Argumentquoting | Standard-Shell-Verhalten. Modellnamen mit Sonderzeichen oder Leerzeichen müssen vom User in Anführungszeichen gesetzt werden (z.B. `--upsert-model-price openai-compatible "gpt-4o-mini" 0.15 0.60`). Java-`args[]`-Parsing verwendet die bereits durch die Shell aufgespaltenen Argumente. |
| Validierung | Identisch zur GUI-Validierung (`ManageModelPricesUseCase`). Verstöße → deutsche Fehlermeldung auf STDERR, Exit-Code 1. |
| Whitelist-Validierung | `--upsert-model-price` lehnt unbekannte Provider ab (nur `openai-compatible`/`anthropic` erlaubt). `--delete-model-price` akzeptiert auch unbekannte Provider, damit verwaiste Einträge entfernbar bleiben. |
| Whitelist-Validierung | `--upsert-model-price` lehnt unbekannte Provider ab (nur `openai-compatible`/`claude` erlaubt). `--delete-model-price` akzeptiert auch unbekannte Provider, damit verwaiste Einträge entfernbar bleiben. |
| Ausgabe-Format | `--list-model-prices`: feste Spaltenbreiten oder einfache Tabular-Ausgabe nach STDOUT, eine Zeile pro Eintrag, Header-Zeile. |
**Exit-Code-Matrix:**
@@ -1277,6 +1288,7 @@ Code-Read).
- [ ] `V2__token_tracking.sql` erstellt und auf V1-DB getestet
- [ ] WAL + `busy_timeout` 5s pro Connection verifiziert
- [ ] Zentrale Connection-Factory eingeführt; alle direkten `DriverManager.getConnection`-Aufrufe in Repository-Adaptern auf Factory umgezogen (Voraussetzung für wirksames WAL + `busy_timeout`)
- [ ] `AiUsageMetadata`, `AiInvocationSuccess` erweitert
- [ ] `ModelPriceEntry` (non-null updatedAt), `ModelPriceView` (nullable)
- [ ] `ModelPriceChangeSet`, `ModelPriceKey`
@@ -1570,13 +1582,13 @@ Code-Read).
| # | Code-Read | Erwartetes Ergebnis | Konsequenz bei Abweichung | Status |
|---|---|---|---|---|
| 1 | `ai_provider`-Werte in `processing_attempt` | `openai-compatible`, `anthropic` | Default-Inserts und Lookup-Logik anpassen | offen |
| 2 | Primärschlüsselspalte `processing_attempt.id` | `id` (AUTOINCREMENT, monoton) | Pseudo-SQL und Tie-Breaker anpassen | offen |
| 3 | Insert-Pfad für `processing_attempt` | Eine zentrale Stelle | Hook für Tokens und Snapshot dort einbauen | offen |
| 4 | Stabilität von `last_known_source_file_name` | Wird nach Initial-Scan nicht überschrieben | Tooltip ggf. anpassen | offen |
| 5 | SQLite-JDBC-Window-Function-Unterstützung | `ROW_NUMBER() OVER (...)` ab SQLite 3.25 | Korrelierte Subquery als Fallback | offen |
| 6 | Connection-Setup: WAL und `busy_timeout` | Beide aktiv; pro Connection | Im Adapter ergänzen | offen |
| 7 | CLI-Adapter-Modul: Befehlsregistrierung | Vorhandenes Pattern nutzen | An bestehendes Pattern anschließen | offen |
| 1 | `ai_provider`-Werte in `processing_attempt` | `openai-compatible`, `anthropic` | Default-Inserts und Lookup-Logik anpassen | abweichend Provider-Wert ist `claude` (nicht `anthropic`); Spec angepasst |
| 2 | Primärschlüsselspalte `processing_attempt.id` | `id` (AUTOINCREMENT, monoton) | Pseudo-SQL und Tie-Breaker anpassen | bestätigt `id INTEGER PRIMARY KEY AUTOINCREMENT`; einzige Migration: V1 |
| 3 | Insert-Pfad für `processing_attempt` | Eine zentrale Stelle | Hook für Tokens und Snapshot dort einbauen | bestätigt `SqliteProcessingAttemptRepositoryAdapter.save()`, 20 Spalten |
| 4 | Stabilität von `last_known_source_file_name` | Wird nach Initial-Scan nicht überschrieben | Tooltip ggf. anpassen | abweichend Feld mutabel, wird bei jedem Lauf überschrieben; Spec klargestellt |
| 5 | SQLite-JDBC-Window-Function-Unterstützung | `ROW_NUMBER() OVER (...)` ab SQLite 3.25 | Korrelierte Subquery als Fallback | bestätigt sqlite-jdbc 3.45.1.0; SQLite 3.45.1; `ROW_NUMBER()` unterstützt |
| 6 | Connection-Setup: WAL und `busy_timeout` | Beide aktiv; pro Connection | Im Adapter ergänzen | abweichend WAL und `busy_timeout` fehlen; `DriverManager`-Stellen ohne Config; Spec um Connection-Factory-Pflicht ergänzt |
| 7 | CLI-Adapter-Modul: Befehlsregistrierung | Vorhandenes Pattern nutzen | An bestehendes Pattern anschließen | bestätigt Eigenbau-Switch-Case in `CliArgumentParser`; Einhänge-Punkt: vor `default` in switch (~Z.95), neuer `StartupMode` in `BootstrapRunner` |
### Nicht blockierend
@@ -1586,7 +1598,5 @@ Code-Read).
| 9 | Lade-Indikator in der GUI | Wiederverwenden falls zentral | offen |
| 10 | JavaFX `Pagination`-Komponente in der Codebasis | Pattern übernehmen falls vorhanden | offen |
**Hinweis zum Status:** Solange einer der freigabe-blockierenden Reads
mit offen" markiert ist, gilt die Spec als „implementierungsvorbereitend".
Erst wenn alle Reads erledigt sind und die Ergebnisse hier dokumentiert
sind, ist das Freigabe-Gate offen.
**Hinweis zum Status:** Alle freigabe-blockierenden Reads sind erledigt. Das Freigabe-Gate
ist offen. Die Implementierung kann mit AP-A beginnen.