Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm testsuite:fetch
- run: pnpm test

verifier:
Expand All @@ -54,3 +55,8 @@ jobs:
docker compose logs verifier; exit 1
- run: pnpm testsuite:fetch
- run: pnpm verifier:check
- run: pnpm roundtrip:check
- run: pnpm corpus:build
- name: Rebuild corpus and compare hashes (EVAL-01 reproducibility)
run: pnpm corpus:build --no-verify | tee rebuild.log && grep -q 'all hashes identical' rebuild.log
- run: pnpm eval --set clean
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ target/
eval/corpus/generated/
eval/.cache/
coverage/
eval/results/
8 changes: 8 additions & 0 deletions .planning/BACKLOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# Backlog (nicht umsetzen ohne Roadmap-Änderung)

## Aus Phase 2/3 (02.10.2026)

- **Artikelnummer (BT-155) ins Modell.** Die Testsuite nutzt 6–10-stellige Artikelnummern als Positions-ID. Im Korpus werden sie zur Positionsnummer, weil das MVP-Modell keine eigene Spalte hat. Echte Rechnungen brauchen beides.
- **Wert-im-Zitat-Check für kurze Werte schärfen.** Ein Wert wie „1" steht in fast jedem Zeilen-Zitat. Der Zeilen-Gegencheck (Menge × Preis = gedruckter Betrag) fängt die Folgen ab, die Prüfung selbst bleibt aber schwach.
- **Tolerierte Codelisten-Fehler sichtbar machen.** KoSIT meldet z. B. `BR-CL-23` (Einheit „Stück") als `error`, antwortet aber ACCEPTABLE. Im UI als Warnung zeigen, auch wenn unsere Normalisierung den Fall schon abfängt.
- **Bildpfad.** Nemotron-Vision (Nano Omni / Nano 2 VL / Parse) ist in Token Factory nicht freigeschaltet. Optionen: Anfrage im Nebius-Discord `#token-factory-support`, oder Nemotron Parse selbst auf Nebius AI Cloud.
- **City Award.** Laut Devpost-Mail nur für Teilnehmende eines Builders-&-Brews-Treffens. Berlin (29.09.) ist vorbei; prüfen, ob im Oktober noch ein Treffen erreichbar ist.
44 changes: 22 additions & 22 deletions .planning/REQUIREMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- [ ] **OPS-01** *(02.10.: lokal grün, CI-Lauf auf GitHub steht aus)*: pnpm-Monorepo mit `apps/web`, `packages/core`, `services/verifier`, `eval`; TypeScript strict; `pnpm typecheck` und `pnpm test` laufen grün in CI
- [ ] **OPS-02** *(02.10.: Hook + CI-Job angelegt, GitHub-Lauf und Lizenz-Erkennung stehen aus)*: gitleaks in CI und pre-commit; Apache-2.0-LICENSE im Repo-Root, auf GitHub im About-Bereich erkannt
- [x] **OPS-03**: Credits eingelöst (Event-Code, Builder Program, Tavily); Smoke-Test ruft je ein Modell für Extraktion und Reparatur über Token Factory auf
- [ ] **OPS-04**: Zentraler LLM-Client zählt Tokens und Kosten je Aufruf und bricht bei Überschreitung von `DAILY_BUDGET_USD` ab
- [x] **OPS-04**: Zentraler LLM-Client zählt Tokens und Kosten je Aufruf und bricht bei Überschreitung von `DAILY_BUDGET_USD` ab
- [ ] **OPS-05** *(02.10.: `docker compose up verifier` läuft lokal; Koyeb offen, Verifier braucht ~570 MB RAM)*: Deployment `web` (öffentlich) und `verifier` (intern oder Secret-geschützt) auf Koyeb; `docker compose up` startet den Verifier lokal

### Verifier (VER)
Expand All @@ -21,37 +21,37 @@
- [ ] **VER-06** *(Phase 4, nur nach bestandenem Kill-Gate, Timebox 5 h)*: `POST /v1/zugferd/combine` (PDF/A + CII → ZUGFeRD PDF/A-3 via Mustang) und `POST /v1/zugferd/validate` (Mustang, gleiches Fehlerformat wie VER-01); die erzeugte Datei besteht die unabhängige Mustang-CLI-Prüfung

### Korpus & Evaluation (EVAL)
- [ ] **EVAL-01**: `pnpm corpus:build` lädt die Testsuite in gepinnter Version und erzeugt reproduzierbar PDFs in den Sets clean, noisy, mutated mit Ground-Truth-Zuordnung
- [ ] **EVAL-02**: Mindestens 3 eigene HTML-Layouts plus KoSIT-Visualisierung im Set clean
- [ ] **EVAL-03**: Mindestens 10 Mutationstypen (u. a. fehlendes Pflichtfeld, falscher Einheitencode, inkonsistente Summe, falsche USt-Kategorie, fehlende Käuferreferenz)
- [ ] **EVAL-04**: `pnpm eval --set <name>` erzeugt JSON- und Markdown-Report mit Validierungsquote, Feldgenauigkeit pro BT, Iterationen, Felder ohne Herkunft, Kosten, Laufzeit
- [ ] **EVAL-05**: Holdout-Split (~20 %) wird bis Phase 5 nicht ausgewertet
- [ ] **EVAL-06**: Lizenz der Testsuite geprüft; generierte Dateien nur committed, wenn zulässig
- [x] **EVAL-01**: `pnpm corpus:build` lädt die Testsuite in gepinnter Version und erzeugt reproduzierbar PDFs in den Sets clean, noisy, mutated mit Ground-Truth-Zuordnung
- [ ] **EVAL-02** *(02.10.: 3 eigene Layouts per pdfkit statt HTML/Playwright, damit Rebuilds byte-gleich sind; KoSIT-Visualisierung kommt mit dem Render-Endpunkt in Phase 4)*: Mindestens 3 eigene HTML-Layouts plus KoSIT-Visualisierung im Set clean
- [x] **EVAL-03**: Mindestens 10 Mutationstypen (u. a. fehlendes Pflichtfeld, falscher Einheitencode, inkonsistente Summe, falsche USt-Kategorie, fehlende Käuferreferenz)
- [x] **EVAL-04**: `pnpm eval --set <name>` erzeugt JSON- und Markdown-Report mit Validierungsquote, Feldgenauigkeit pro BT, Iterationen, Felder ohne Herkunft, Kosten, Laufzeit
- [x] **EVAL-05**: Holdout-Split (~20 %) wird bis Phase 5 nicht ausgewertet
- [x] **EVAL-06**: Lizenz der Testsuite geprüft; generierte Dateien nur committed, wenn zulässig

### Extraktion (EXT)
- [ ] **EXT-01**: zod-Schema für MVP-BTs laut SRS §3 mit Provenance pro Feld
- [ ] **EXT-02** *(02.10.: Nano Omni ist in Token Factory nicht verfügbar. Entscheidung 02.10.: vorerst Textlayer + Nemotron-Text, siehe ROADMAP Phase 1 Stand)*: Nano Omni liefert schemakonformes JSON aus Seitenbildern; ungültiges JSON führt zu genau einem Retry, danach Fehlerzustand
- [ ] **EXT-03**: Evidence-Check verwirft Felder, deren Zitat bei vorhandenem Textlayer nicht im Seitentext steht
- [ ] **EXT-04**: Normalisierung gegen Codelisten (Einheiten, Länder, USt-Kategorien, Zahlungsarten); unbekannte Werte werden `missing`
- [ ] **EXT-05**: Gedruckte Summen werden separat extrahiert und nur für den Konsistenzabgleich genutzt
- [x] **EXT-01**: zod-Schema für MVP-BTs laut SRS §3 mit Provenance pro Feld
- [x] **EXT-02** *(02.10.: umgesetzt mit Textlayer + Nemotron Super statt Nano Omni, das in Token Factory fehlt)*: Nano Omni liefert schemakonformes JSON aus Seitenbildern; ungültiges JSON führt zu genau einem Retry, danach Fehlerzustand
- [x] **EXT-03**: Evidence-Check verwirft Felder, deren Zitat bei vorhandenem Textlayer nicht im Seitentext steht
- [x] **EXT-04**: Normalisierung gegen Codelisten (Einheiten, Länder, USt-Kategorien, Zahlungsarten); unbekannte Werte werden `missing`
- [x] **EXT-05**: Gedruckte Summen werden separat extrahiert und nur für den Konsistenzabgleich genutzt

### Kern & Ausgabe (CORE)
- [ ] **CORE-01**: Deterministische Ableitung von BT-131, BG-23 und BG-22 mit Decimal-Arithmetik; Unit-Tests gegen alle validen Testsuite-Dateien im Scope
- [ ] **CORE-02**: CII-Builder erzeugt aus Ground-Truth-Modellen XML, das VER-01 als valide meldet (Roundtrip-Test ohne LLM)
- [ ] **CORE-03**: Konsistenzprüfung meldet Abweichung zwischen gedruckten und berechneten Summen als Warnung
- [x] **CORE-01**: Deterministische Ableitung von BT-131, BG-23 und BG-22 mit Decimal-Arithmetik; Unit-Tests gegen alle validen Testsuite-Dateien im Scope
- [x] **CORE-02**: CII-Builder erzeugt aus Ground-Truth-Modellen XML, das VER-01 als valide meldet (Roundtrip-Test ohne LLM)
- [x] **CORE-03**: Konsistenzprüfung meldet Abweichung zwischen gedruckten und berechneten Summen als Warnung
- [ ] **CORE-04**: Ausgabe XRechnung-CII als Download
- [ ] **CORE-05** *(Phase 4, nur nach bestandenem Kill-Gate)*: Ausgabe ZUGFeRD PDF/A-3 (Profil EN16931) als Download

### Reparatur (REP)
- [ ] **REP-01**: Ultra erhält Regelverletzungen, aktuelles Modell und Seitenbilder und liefert RFC-6902-Patches mit Provenance oder die Kennzeichnung "nicht aus Dokument lösbar"
- [ ] **REP-02**: Patch-Guard verwirft Patches auf derived-Felder, ohne Provenance oder mit nicht nachweisbarem Zitat; jede Verwerfung im Audit-Log
- [ ] **REP-03**: Maximal 3 Iterationen; Abbruch, wenn die Fehlerzahl nicht sinkt
- [ ] **REP-04**: NEEDS_INPUT listet nur tatsächlich fehlende Felder mit Erklärung; Nutzereingabe erhält Provenance `user` und löst neue Validierung aus
- [x] **REP-01** *(Textlayer statt Seitenbilder)*: Ultra erhält Regelverletzungen, aktuelles Modell und Seitenbilder und liefert RFC-6902-Patches mit Provenance oder die Kennzeichnung "nicht aus Dokument lösbar"
- [x] **REP-02**: Patch-Guard verwirft Patches auf derived-Felder, ohne Provenance oder mit nicht nachweisbarem Zitat; jede Verwerfung im Audit-Log
- [x] **REP-03**: Maximal 3 Iterationen; Abbruch, wenn die Fehlerzahl nicht sinkt
- [x] **REP-04**: NEEDS_INPUT listet nur tatsächlich fehlende Felder mit Erklärung; Nutzereingabe erhält Provenance `user` und löst neue Validierung aus

### Audit (AUD)
- [ ] **AUD-01**: JSONL-Hash-Kette nach SRS §6 (JCS, SHA-256, prevHash) für jeden Zustandsübergang
- [ ] **AUD-02**: Modell-Events enthalten Modell-ID, Prompt-Hash, Tokens, Kosten, keine Rechnungsinhalte
- [ ] **AUD-03**: `pnpm audit:verify` erkennt jede Manipulation an Events oder Ausgabedateien (Tests mit manipulierten Logs)
- [x] **AUD-01**: JSONL-Hash-Kette nach SRS §6 (JCS, SHA-256, prevHash) für jeden Zustandsübergang
- [x] **AUD-02**: Modell-Events enthalten Modell-ID, Prompt-Hash, Tokens, Kosten, keine Rechnungsinhalte
- [x] **AUD-03**: `pnpm audit:verify` erkennt jede Manipulation an Events oder Ausgabedateien (Tests mit manipulierten Logs)

### Erklärung (EXP)
- [ ] **EXP-01**: Lokale Erklärungstabelle für die im Korpus auftretenden Regel-IDs (EN/DE)
Expand Down
22 changes: 18 additions & 4 deletions .planning/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te
## Phases

- [ ] **Phase 1: Fundament & Verifier** — Repo, Credits, Java-Verifier, ZUGFeRD-Spike (17.–23.09., ~10 h)
- [ ] **Phase 2: Korpus & Ground Truth** — Testdaten, deterministischer Kern ohne LLM, Eval-Harness (24.–30.09., ~9 h)
- [x] **Phase 2: Korpus & Ground Truth** (02.10.) — Testdaten, deterministischer Kern ohne LLM, Eval-Harness (24.–30.09., ~9 h)
- [ ] **Phase 3: Kernschleife** — Extraktion, Reparatur, Rückfrage, Audit-Log (01.–10.10., ~14 h)
- [ ] **Phase 4: Produkt** — Web-UI, Erklärungen, Galerie, Deployment, danach ZUGFeRD-Ausgabe (11.–21.10., ~11 h + ZUGFeRD-Timebox 5 h)
- [ ] **Phase 5: Einreichung** — Holdout-Benchmark, README, Video, Devpost (22.–28.10., ~6 h)
Expand Down Expand Up @@ -52,7 +52,14 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te
3. Roundtrip Ground Truth → CII-Builder → Verifier ist für alle Korpusrechnungen im Scope valide
4. Eval-Report läuft durch (mit Dummy-Extraktor), Holdout ist abgetrennt
**Plans**: TBD
**Hinweis**: Builders & Brews Berlin am Di 29.09. optional (Credits, Kontakte). Keine Voraussetzung für den City Award.
**Hinweis**: ~~Builders & Brews Berlin keine Voraussetzung für den City Award~~ — laut Devpost-Mail ist der City Award nur für Teilnehmende eines Treffens. Siehe BACKLOG.

**Stand 02.10.2026 (Phase 2 abgeschlossen)**
- Scope: 19 Testsuite-Rechnungen (380, EUR, ohne Nachlässe/Zuschläge/Vorauszahlung/Rundung, Zahlung per Überweisung 58 oder ohne). Liste in `eval/src/scope.ts`.
- CORE-01: abgeleitete Summen = gedruckte Summen für 19/19 (USt ±0,01 nach BR-CO-17; 01.06_minimal druckt 757,41 statt 757,40). CORE-02: Rundtest 19/19 ACCEPTABLE.
- Korpus: clean 45 (3 Layouts), noisy 30 (2 Varianten), mutated 26 (13 Typen, jeder gegen den Verifier geprüft), Holdout 20 (01.03a, 01.09a, 01.10a, 01.14a per Hash). Rebuild byte-identisch.
- Befunde Verifier: `BR-CL-23` (Einheit „Stück") wird gemeldet, aber ACCEPTABLE. `BR-CO-25` (weder Fälligkeit noch Zahlungsbedingungen) greift nicht.
- Testdaten-Hygiene nach den ersten Messungen: Platzhalter („[Seller name]", „nicht vorhanden", „-", Rechnungsnummer „Rechnungsnummer") durch erfundene Werte ersetzt; Positions-IDs > 4 Zeichen werden zur Positionsnummer (das MVP-Modell hat keine Artikelnummer-Spalte). Weil das nach Sicht auf Ergebnisse geschah, zählt für den Benchmark nur der Holdout.

### Phase 3: Kernschleife
**Goal**: Aus einem PDF entsteht über Extraktion, Validierung und Reparatur eine valide XRechnung mit lückenloser Herkunft und Audit-Log.
Expand All @@ -66,6 +73,13 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te
**Plans**: TBD
**Arbeitsweise**: CLI zuerst (`pnpm run:invoice <pdf>`), UI erst in Phase 4

**Stand 02.10.2026 (Kill-Gate erfüllt, 8 Tage vor Termin)**
- Pipeline `pnpm eval --set <set> --pipeline agent`: Textlayer → Nemotron Super (JSON-Schema) → Zitat-Check → Normalisierung → Form-/Rollen-Regeln → Ableitung → Zeilen-Gegencheck → CII → KoSIT, bis 3 Reparaturrunden mit Nemotron Ultra hinter dem Patch-Guard, Audit-Kette.
- Messung (letzter Lauf): clean 41/45 = 91 % gültig mit korrekten BT-112/BT-115 und voller Herkunft (Kill-Gate ≥ 80 %), Feldgenauigkeit 98,4 %, 0,26 ct/Rechnung, Median 7,4 s. noisy 23/30 = 77 %, 98,8 %. mutated 25/26 = 96 % wie erwartet.
- Seit dem Zeilen-Gegencheck keine einzige valide Ausgabe mit falschem Betrag; Unsicheres endet in NEEDS_INPUT.
- Restfehler noisy: alle NEEDS_INPUT (Telefon/Land/Stadt bei Variante b: kleine Schrift, Seitenumbruch). Restfehler mutated: Verkäufer-Stadt aus gleichlautender Käuferadresse (gleiche PLZ 12345 in der Testsuite).
- Offen in Phase 3: OPS-04 ✓, CORE-04 (Download) mit UI; `pnpm run:invoice <pdf>`-CLI.

### Phase 4: Produkt
**Goal**: Eine Jurorin kann ohne Anleitung eine Beispielrechnung umwandeln, Fehler verstehen und alle Ergebnisse herunterladen.
**Depends on**: Phase 3
Expand Down Expand Up @@ -95,7 +109,7 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te
| Phase | Plans Complete | Status | Ist-Stunden | Completed |
|---|---|---|---|---|
| 1. Fundament & Verifier | — | In Progress (02.10.) | ? / 10 | - |
| 2. Korpus & Ground Truth | 0/TBD | Not started | 0 / 9 | - |
| 3. Kernschleife | 0/TBD | Not started | 0 / 14 | - |
| 2. Korpus & Ground Truth | — | Complete | ? / 9 | 02.10. |
| 3. Kernschleife | — | Kill-Gate erfüllt, Rest offen | ? / 14 | - |
| 4. Produkt | 0/TBD | Not started | 0 / 11 | - |
| 5. Einreichung | 0/TBD | Not started | 0 / 6 | - |
14 changes: 13 additions & 1 deletion eval/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@
"typecheck": "tsc -p tsconfig.json",
"test": "vitest run --passWithNoTests",
"testsuite:fetch": "tsx scripts/fetch-testsuite.ts",
"verifier:check": "tsx --env-file-if-exists=../.env scripts/verifier-check.ts"
"verifier:check": "tsx --env-file-if-exists=../.env scripts/verifier-check.ts",
"roundtrip:check": "tsx --env-file-if-exists=../.env scripts/roundtrip-check.ts",
"corpus:build": "tsx --env-file-if-exists=../.env scripts/corpus-build.ts",
"eval": "tsx --env-file-if-exists=../.env scripts/run-eval.ts"
},
"dependencies": {
"@verdict/core": "workspace:*",
"decimal.js": "^10.6.0",
"pdfkit": "^0.20.2"
},
"devDependencies": {
"@types/pdfkit": "^0.17.6",
"pdfjs-dist": "^6.3.289"
}
}
Loading
Loading