diff --git a/.gitignore b/.gitignore index b74dd09..d8d1a54 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,9 @@ eval/corpus/generated/ eval/.cache/ coverage/ eval/results/ +apps/web/public/pdf.worker.min.mjs +apps/web/.next/ +apps/web/.env.local +apps/web/.impeccable/review/ +apps/web/.impeccable/live/ +apps/web/.impeccable/questions/ diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index d24032b..c4e4748 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -39,7 +39,7 @@ - [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 +- [x] **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) @@ -58,13 +58,13 @@ - [ ] **EXP-02**: Fehlt ein Eintrag, erzeugt Nano/Super eine Erklärung; bei Bedarf Tavily-Aufruf mit angezeigter Quelle; Cache pro Regel-ID ### Web-UI (UI) -- [ ] **UI-01**: Upload mit Limits laut NFR-04 und klaren Fehlermeldungen -- [ ] **UI-02**: Live-Stream der Zustände per SSE mit Dauer je Schritt -- [ ] **UI-03**: Ergebnisansicht: Validator-Status, Regelverletzungen mit Erklärung, angewandte Patches mit Beleg, Konsistenzwarnungen -- [ ] **UI-04**: Formular für NEEDS_INPUT -- [ ] **UI-05**: Downloads: XRechnung-XML, ZUGFeRD-PDF, Audit-Log -- [ ] **UI-06**: Beispielgalerie mit mindestens 6 vorberechneten Läufen (clean, noisy, mutated), funktioniert auch bei erschöpftem Budget -- [ ] **UI-07**: Rate-Limit pro IP, Tagesbudget-Anzeige, Fallback auf Galerie +- [x] **UI-01**: Upload mit Limits laut NFR-04 und klaren Fehlermeldungen +- [x] **UI-02**: Live-Stream der Zustände per SSE mit Dauer je Schritt +- [x] **UI-03**: Ergebnisansicht: Validator-Status, Regelverletzungen mit Erklärung, angewandte Patches mit Beleg, Konsistenzwarnungen +- [x] **UI-04**: Formular für NEEDS_INPUT +- [x] **UI-05** *(XML + Audit-Log; ZUGFeRD-PDF folgt mit CORE-05)*: Downloads: XRechnung-XML, ZUGFeRD-PDF, Audit-Log +- [x] **UI-06**: Beispielgalerie mit mindestens 6 vorberechneten Läufen (clean, noisy, mutated), funktioniert auch bei erschöpftem Budget +- [x] **UI-07**: Rate-Limit pro IP, Tagesbudget-Anzeige, Fallback auf Galerie ### Einreichung (SUB) - [ ] **SUB-01**: README (EN) mit Setup, Architektur, Nemotron-/Token-Factory-Nutzung, Benchmark-Zahlen inkl. Holdout diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index e977a77..4192b44 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -93,6 +93,13 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te **Plans**: TBD **Skill**: `frontend-design` für UI-Arbeit nutzen +**Stand 02.10.2026 (UI fertig, Finish-Review „ship")** +- Gestaltet mit `impeccable` (Operate-Modus, code-first). Richtung per Auslosung + Patricks Wahl: „Laufzettel & Prüfstempel" (Seed b067d9c4). Vertrag: `apps/web/.impeccable/surfaces/app-page-tsx.md`, System: `apps/web/DESIGN.md`, Produkt: `apps/web/PRODUCT.md`. +- Next.js 16: `POST /api/run` (SSE, Limits, Rate-Limit, Tagesbudget, nichts gespeichert), `GET /api/budget`. Galerie: 7 aufgezeichnete echte Läufe (`pnpm --filter @verdict/web gallery:build`), funktioniert ohne Budget. +- Unabhängiger Finish-Reviewer: Runde 1 „fix" (8 Punkte), Runde 2 „fix" (3 Regressionen), Runde 3 „ship". Dabei gefunden und im Kern behoben: Zitate mit wörtlichem `\n` wurden fälschlich abgelehnt; KoSIT-Ablehnungen wie BR-DE-1 werden jetzt zur gezielten Rückfrage. +- Produktions-Build und `next start` mit echter PDF geprüft. +- Offen: Koyeb-Deployment (OPS-05), Galerie-Lauf mit echter Ablehnung fürs Video (Reviewer N2), ZUGFeRD (VER-06/CORE-05, nach Kill-Gate erlaubt), README/Video (Phase 5). + ### Phase 5: Einreichung **Goal**: Vollständige, regelkonforme Einreichung mit belastbaren Zahlen, zwei Tage vor Deadline. **Depends on**: Phase 4 @@ -111,5 +118,5 @@ Fünf Phasen vom 17.09. bis 28.10.2026, ~50 h Gesamtaufwand. Die riskantesten Te | 1. Fundament & Verifier | — | In Progress (02.10.) | ? / 10 | - | | 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 | - | +| 4. Produkt | — | UI fertig (02.10.), Deployment + ZUGFeRD offen | ? / 11 | - | | 5. Einreichung | 0/TBD | Not started | 0 / 6 | - | diff --git a/apps/web/.impeccable/design.json b/apps/web/.impeccable/design.json new file mode 100644 index 0000000..137a0c0 --- /dev/null +++ b/apps/web/.impeccable/design.json @@ -0,0 +1,636 @@ +{ + "schemaVersion": 2, + "generatedAt": "2026-10-02T21:30:48+00:00", + "title": "Design System: Verdict", + "extensions": { + "colorMeta": { + "paper": { + "role": "neutral", + "displayName": "Recycled Paper", + "canonical": "#f2f3ee", + "oklch": "oklch(96.2% 0.007 115.7)", + "tonalRamp": [ + "oklch(15% 0.007 115.7)", + "oklch(25% 0.007 115.7)", + "oklch(35% 0.007 115.7)", + "oklch(45% 0.007 115.7)", + "oklch(58% 0.007 115.7)", + "oklch(70% 0.007 115.7)", + "oklch(82% 0.007 115.7)", + "oklch(95% 0.007 115.7)" + ] + }, + "sheet": { + "role": "neutral", + "displayName": "Form Sheet", + "canonical": "#fbfbf8", + "oklch": "oklch(98.7% 0.004 106.5)", + "tonalRamp": [ + "oklch(15% 0.004 106.5)", + "oklch(25% 0.004 106.5)", + "oklch(35% 0.004 106.5)", + "oklch(45% 0.004 106.5)", + "oklch(58% 0.004 106.5)", + "oklch(70% 0.004 106.5)", + "oklch(82% 0.004 106.5)", + "oklch(95% 0.004 106.5)" + ] + }, + "page-white": { + "role": "neutral", + "displayName": "Page White", + "canonical": "#ffffff", + "oklch": "oklch(100.0% 0.000 89.9)", + "tonalRamp": [ + "oklch(15% 0.000 89.9)", + "oklch(25% 0.000 89.9)", + "oklch(35% 0.000 89.9)", + "oklch(45% 0.000 89.9)", + "oklch(58% 0.000 89.9)", + "oklch(70% 0.000 89.9)", + "oklch(82% 0.000 89.9)", + "oklch(95% 0.000 89.9)" + ] + }, + "rule": { + "role": "neutral", + "displayName": "Hairline", + "canonical": "#d5d7cf", + "oklch": "oklch(87.5% 0.011 118.0)", + "tonalRamp": [ + "oklch(15% 0.011 118.0)", + "oklch(25% 0.011 118.0)", + "oklch(35% 0.011 118.0)", + "oklch(45% 0.011 118.0)", + "oklch(58% 0.011 118.0)", + "oklch(70% 0.011 118.0)", + "oklch(82% 0.011 118.0)", + "oklch(95% 0.011 118.0)" + ] + }, + "rule-strong": { + "role": "neutral", + "displayName": "Strong Hairline", + "canonical": "#b6b9ae", + "oklch": "oklch(78.0% 0.016 119.2)", + "tonalRamp": [ + "oklch(15% 0.016 119.2)", + "oklch(25% 0.016 119.2)", + "oklch(35% 0.016 119.2)", + "oklch(45% 0.016 119.2)", + "oklch(58% 0.016 119.2)", + "oklch(70% 0.016 119.2)", + "oklch(82% 0.016 119.2)", + "oklch(95% 0.016 119.2)" + ] + }, + "ink": { + "role": "neutral", + "displayName": "Ink", + "canonical": "#1c1f22", + "oklch": "oklch(23.7% 0.007 248.1)", + "tonalRamp": [ + "oklch(15% 0.007 248.1)", + "oklch(25% 0.007 248.1)", + "oklch(35% 0.007 248.1)", + "oklch(45% 0.007 248.1)", + "oklch(58% 0.007 248.1)", + "oklch(70% 0.007 248.1)", + "oklch(82% 0.007 248.1)", + "oklch(95% 0.007 248.1)" + ] + }, + "ink-2": { + "role": "neutral", + "displayName": "Ink 2", + "canonical": "#4a4f54", + "oklch": "oklch(42.5% 0.011 248.1)", + "tonalRamp": [ + "oklch(15% 0.011 248.1)", + "oklch(25% 0.011 248.1)", + "oklch(35% 0.011 248.1)", + "oklch(45% 0.011 248.1)", + "oklch(58% 0.011 248.1)", + "oklch(70% 0.011 248.1)", + "oklch(82% 0.011 248.1)", + "oklch(95% 0.011 248.1)" + ] + }, + "ink-3": { + "role": "neutral", + "displayName": "Ink 3", + "canonical": "#62676d", + "oklch": "oklch(54.3% 0.011 252.9)", + "tonalRamp": [ + "oklch(15% 0.011 252.9)", + "oklch(25% 0.011 252.9)", + "oklch(35% 0.011 252.9)", + "oklch(45% 0.011 252.9)", + "oklch(58% 0.011 252.9)", + "oklch(70% 0.011 252.9)", + "oklch(82% 0.011 252.9)", + "oklch(95% 0.011 252.9)" + ] + }, + "office": { + "role": "tertiary", + "displayName": "Office Green", + "canonical": "#2f5d4a", + "oklch": "oklch(43.9% 0.060 165.0)", + "tonalRamp": [ + "oklch(15% 0.060 165.0)", + "oklch(25% 0.060 165.0)", + "oklch(35% 0.060 165.0)", + "oklch(45% 0.060 165.0)", + "oklch(58% 0.060 165.0)", + "oklch(70% 0.060 165.0)", + "oklch(82% 0.060 165.0)", + "oklch(95% 0.060 165.0)" + ] + }, + "office-2": { + "role": "tertiary", + "displayName": "Office Green Light", + "canonical": "#3c6f59", + "oklch": "oklch(49.9% 0.066 164.3)", + "tonalRamp": [ + "oklch(15% 0.066 164.3)", + "oklch(25% 0.066 164.3)", + "oklch(35% 0.066 164.3)", + "oklch(45% 0.066 164.3)", + "oklch(58% 0.066 164.3)", + "oklch(70% 0.066 164.3)", + "oklch(82% 0.066 164.3)", + "oklch(95% 0.066 164.3)" + ] + }, + "office-ink": { + "role": "tertiary", + "displayName": "Office Ink", + "canonical": "#e8efe9", + "oklch": "oklch(94.5% 0.011 149.9)", + "tonalRamp": [ + "oklch(15% 0.011 149.9)", + "oklch(25% 0.011 149.9)", + "oklch(35% 0.011 149.9)", + "oklch(45% 0.011 149.9)", + "oklch(58% 0.011 149.9)", + "oklch(70% 0.011 149.9)", + "oklch(82% 0.011 149.9)", + "oklch(95% 0.011 149.9)" + ] + }, + "stamp": { + "role": "primary", + "displayName": "Stamp Violet", + "canonical": "#4536a8", + "oklch": "oklch(42.3% 0.174 282.0)", + "tonalRamp": [ + "oklch(15% 0.160 282.0)", + "oklch(25% 0.160 282.0)", + "oklch(35% 0.174 282.0)", + "oklch(45% 0.174 282.0)", + "oklch(58% 0.174 282.0)", + "oklch(70% 0.174 282.0)", + "oklch(82% 0.174 282.0)", + "oklch(95% 0.160 282.0)" + ] + }, + "stamp-wash": { + "role": "primary", + "displayName": "Stamp Wash", + "canonical": "#e7e4f6", + "oklch": "oklch(92.7% 0.024 293.6)", + "tonalRamp": [ + "oklch(15% 0.024 293.6)", + "oklch(25% 0.024 293.6)", + "oklch(35% 0.024 293.6)", + "oklch(45% 0.024 293.6)", + "oklch(58% 0.024 293.6)", + "oklch(70% 0.024 293.6)", + "oklch(82% 0.024 293.6)", + "oklch(95% 0.024 293.6)" + ] + }, + "void": { + "role": "secondary", + "displayName": "Void Red", + "canonical": "#c2382b", + "oklch": "oklch(54.6% 0.177 29.4)", + "tonalRamp": [ + "oklch(15% 0.160 29.4)", + "oklch(25% 0.160 29.4)", + "oklch(35% 0.177 29.4)", + "oklch(45% 0.177 29.4)", + "oklch(58% 0.177 29.4)", + "oklch(70% 0.177 29.4)", + "oklch(82% 0.177 29.4)", + "oklch(95% 0.160 29.4)" + ] + }, + "void-wash": { + "role": "secondary", + "displayName": "Void Wash", + "canonical": "#f6e3df", + "oklch": "oklch(93.0% 0.022 31.8)", + "tonalRamp": [ + "oklch(15% 0.022 31.8)", + "oklch(25% 0.022 31.8)", + "oklch(35% 0.022 31.8)", + "oklch(45% 0.022 31.8)", + "oklch(58% 0.022 31.8)", + "oklch(70% 0.022 31.8)", + "oklch(82% 0.022 31.8)", + "oklch(95% 0.022 31.8)" + ] + }, + "tab": { + "role": "tertiary", + "displayName": "Folder-Tab Yellow", + "canonical": "#e7c43f", + "oklch": "oklch(82.8% 0.150 93.8)", + "tonalRamp": [ + "oklch(15% 0.150 93.8)", + "oklch(25% 0.150 93.8)", + "oklch(35% 0.150 93.8)", + "oklch(45% 0.150 93.8)", + "oklch(58% 0.150 93.8)", + "oklch(70% 0.150 93.8)", + "oklch(82% 0.150 93.8)", + "oklch(95% 0.150 93.8)" + ] + }, + "tab-wash": { + "role": "tertiary", + "displayName": "Tab Wash", + "canonical": "#f7eec4", + "oklch": "oklch(94.6% 0.055 97.3)", + "tonalRamp": [ + "oklch(15% 0.055 97.3)", + "oklch(25% 0.055 97.3)", + "oklch(35% 0.055 97.3)", + "oklch(45% 0.055 97.3)", + "oklch(58% 0.055 97.3)", + "oklch(70% 0.055 97.3)", + "oklch(82% 0.055 97.3)", + "oklch(95% 0.055 97.3)" + ] + }, + "tab-ink": { + "role": "tertiary", + "displayName": "Tab Ink", + "canonical": "#5a4708", + "oklch": "oklch(40.7% 0.079 90.1)", + "tonalRamp": [ + "oklch(15% 0.079 90.1)", + "oklch(25% 0.079 90.1)", + "oklch(35% 0.079 90.1)", + "oklch(45% 0.079 90.1)", + "oklch(58% 0.079 90.1)", + "oklch(70% 0.079 90.1)", + "oklch(82% 0.079 90.1)", + "oklch(95% 0.079 90.1)" + ] + }, + "quote": { + "role": "neutral", + "displayName": "Quote Highlighter", + "canonical": "#f3e7a0", + "oklch": "oklch(92.1% 0.091 99.8)", + "tonalRamp": [ + "oklch(15% 0.091 99.8)", + "oklch(25% 0.091 99.8)", + "oklch(35% 0.091 99.8)", + "oklch(45% 0.091 99.8)", + "oklch(58% 0.091 99.8)", + "oklch(70% 0.091 99.8)", + "oklch(82% 0.091 99.8)", + "oklch(95% 0.091 99.8)" + ] + } + }, + "typographyMeta": { + "stamp-title": { + "displayName": "Stamp Title", + "purpose": "VERIFIED / REJECTED / RETURNED inside the verdict stamp; 2.25rem below 640px, uppercase." + }, + "display": { + "displayName": "Display", + "purpose": "The single idle-state headline; 2rem below 640px." + }, + "figure": { + "displayName": "Figure", + "purpose": "The computed BT-112 total beside the stamp; one per result." + }, + "wordmark": { + "displayName": "Wordmark", + "purpose": "VERDICT in the top bar, uppercase." + }, + "title-caps": { + "displayName": "Title Caps", + "purpose": "Fact group headings, Recorded runs, Refused not hidden, Principles terms; uppercase. Routing slip heading at 1rem/700." + }, + "body-lg": { + "displayName": "Body Large", + "purpose": "Idle explainer paragraph and drop-zone prompt." + }, + "body": { + "displayName": "Body", + "purpose": "Fact labels and values, station names (500), explanations at 44-58ch." + }, + "label-caps": { + "displayName": "Label Caps", + "purpose": "Slip column headers, stamp sub-lines (0.14em), initials, VOID marks and tags (700, 0.12em); uppercase." + }, + "note": { + "displayName": "Note", + "purpose": "Station notes, validator wording, recorded-run stories." + }, + "mono": { + "displayName": "Mono", + "purpose": "BT codes, hashes, durations, tokens, costs, unit codes, provenance chips; 0.875rem for totals ledger and needs-input field." + } + }, + "shadows": [ + { + "name": "invoice-page", + "value": "0 1px 2px rgba(28,31,34,0.08), 0 8px 24px -12px rgba(28,31,34,0.18)", + "purpose": "The rendered invoice page lifted off the desk; the only shadow in the system." + } + ], + "blendModes": [ + { + "name": "ink-into-paper", + "value": "mix-blend-mode: multiply", + "purpose": "Verdict stamp and the quote highlights on the PDF page sit in the paper." + } + ], + "ruleWeights": [ + { + "name": "hairline", + "value": "1px solid #d5d7cf", + "purpose": "Row dividers in lists and tables." + }, + { + "name": "hairline-strong", + "value": "1px solid #b6b9ae", + "purpose": "Group top rules, column-header rules, result-head closing rule." + }, + { + "name": "structural", + "value": "2px solid #1c1f22", + "purpose": "Routing-slip head and totals ledger." + }, + { + "name": "pending-dashed", + "value": "1px dashed #b6b9ae / 2px dashed (drop zone)", + "purpose": "Not yet filled in: pending initials, idle drop zone." + }, + { + "name": "second-pass-dashed", + "value": "2px dashed rgba(90,71,8,0.5)", + "purpose": "Divider where a resumed run continues on the same slip." + }, + { + "name": "stamp-double", + "value": "3px outer + 3px gap + 1.5px inner", + "purpose": "Verdict stamp frame." + }, + { + "name": "stamp-small", + "value": "1.5px", + "purpose": "Initials, VOID marks, outcome tags; void strike-through decoration thickness." + } + ], + "rotations": [ + { + "name": "stamp-verified", + "value": "-4deg" + }, + { + "name": "stamp-rejected", + "value": "3deg" + }, + { + "name": "stamp-returned", + "value": "-3deg" + }, + { + "name": "void-mark", + "value": "-3deg" + }, + { + "name": "slip-initials", + "value": "-2deg" + } + ], + "motion": [ + { + "name": "stamp-press", + "value": "420ms cubic-bezier(0.16, 1, 0.3, 1) both; 0%: opacity 0, scale 1.35, blur 2px; 60%: opacity 1, scale 0.97, blur 0; 100%: scale 1 (rotation held via --stamp-rot)", + "purpose": "The verdict stamp being pressed onto the sheet." + }, + { + "name": "slip-in", + "value": "220ms cubic-bezier(0.16, 1, 0.3, 1) both; translateY(-4px) and opacity 0 to rest", + "purpose": "Each new routing-slip row as a station signs." + }, + { + "name": "quote-focus", + "value": "background-color 200ms", + "purpose": "Focus box on the PDF page following the hovered fact." + }, + { + "name": "color-swap", + "value": "150ms cubic-bezier(0.4, 0, 0.2, 1) (Tailwind transition-colors)", + "purpose": "Button, drop-zone and row hover/focus." + }, + { + "name": "pending-pulse", + "value": "Tailwind animate-pulse, 2s cubic-bezier(0.4, 0, 0.6, 1) infinite", + "purpose": "Facts skeleton, pending slip row, loading page." + }, + { + "name": "reduced-motion", + "value": "@media (prefers-reduced-motion: reduce) { .stamp-press, .slip-in { animation: none } }", + "purpose": "Stamp and slip entrances switch off; the stamp keeps its rotation." + } + ], + "breakpoints": [ + { + "name": "sm", + "value": "640px" + }, + { + "name": "md", + "value": "768px" + }, + { + "name": "lg", + "value": "1024px", + "purpose": "Three-column desk layout; below it, work-area tabs." + }, + { + "name": "container", + "value": "1600px max width" + } + ], + "grid": { + "desktop": "minmax(0,1fr) minmax(0,1.15fr) minmax(16rem,0.75fr)", + "slipRow": "1fr 3.75rem 4.25rem", + "factRow": "4.25rem minmax(0,9rem) 1fr 2.25rem", + "factRowMobile": "3.5rem 1fr 2rem" + } + }, + "components": [ + { + "name": "Verdict Stamp (verified)", + "kind": "custom", + "refersTo": "verdict-stamp-verified", + "description": "Signature move: the result pressed onto the sheet as a double-ruled inspection stamp with validator version, date and report hash; rejected uses void red at +3deg, returned uses tab-ink at -3deg.", + "html": "
Verified
KoSIT 1.6.3 · XRechnung 3.0.2
2026-10-02
report 30aec56f4f
", + "css": ".ds-stamp { display:inline-block; border:3px solid var(--color-stamp, #4536a8); padding:3px; color:var(--color-stamp, #4536a8); mix-blend-mode:multiply; transform:rotate(var(--stamp-rot)); animation:ds-stamp-press 420ms cubic-bezier(0.16, 1, 0.3, 1) both; } .ds-stamp-inner { border:1.5px solid var(--color-stamp, #4536a8); padding:10px 20px; text-align:center; } .ds-stamp-title { font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:2.75rem; line-height:1; font-weight:700; letter-spacing:0.08em; text-transform:uppercase; } .ds-stamp-line { margin-top:4px; font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:0.75rem; line-height:1rem; font-weight:600; letter-spacing:0.14em; text-transform:uppercase; } .ds-stamp-hash { margin-top:4px; font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.75rem; line-height:1rem; } @keyframes ds-stamp-press { 0% { opacity:0; transform:rotate(var(--stamp-rot)) scale(1.35); filter:blur(2px); } 60% { opacity:1; transform:rotate(var(--stamp-rot)) scale(0.97); filter:blur(0); } 100% { opacity:1; transform:rotate(var(--stamp-rot)) scale(1); } } @media (prefers-reduced-motion: reduce) { .ds-stamp { animation:none; } }" + }, + { + "name": "Void Mark with struck value", + "kind": "custom", + "refersTo": "void-mark", + "description": "Nothing disappears, it cancels: the refused value stays on record, struck through in void red, with a small rotated VOID stamp.", + "html": "
1.005,00 € Not used
", + "css": ".ds-void-row { display:flex; align-items:center; gap:8px; font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; color:var(--color-ink-2, #4a4f54); } .ds-void-struck { color:var(--color-ink-3, #62676d); text-decoration:line-through; text-decoration-color:var(--color-void, #c2382b); text-decoration-thickness:1.5px; } .ds-void-mark { display:inline-block; transform:rotate(-3deg); border:1.5px solid var(--color-void, #c2382b); padding:0 6px; font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:0.75rem; line-height:1rem; font-weight:700; letter-spacing:0.12em; text-transform:uppercase; color:var(--color-void, #c2382b); }" + }, + { + "name": "Routing Slip Row", + "kind": "custom", + "refersTo": "slip-row", + "description": "One station on the Laufzettel: name, audit note, a duration bar whose length is real time, rotated sign-off initials coloured by actor, and the duration in mono.", + "html": "
StationSignedTook
Read by Nemotron Super

1515 → 2119 tokens · $0.00236

NEM·S8.27 s
Official KoSIT validator

accepted · report 30aec56f4f

KoSIT85 ms
Returned for your input

1 fact not on the document

RTN–
", + "css": ".ds-slip { font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); max-width:360px; } .ds-slip-head, .ds-slip-row { display:grid; grid-template-columns:1fr 3.75rem 4.25rem; column-gap:12px; } .ds-slip-head { border-top:2px solid var(--color-ink, #1c1f22); border-bottom:1px solid var(--color-rule-strong, #b6b9ae); padding:6px 0; font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:0.75rem; line-height:1rem; font-weight:600; letter-spacing:0.1em; text-transform:uppercase; color:var(--color-ink-3, #62676d); } .ds-slip-row { align-items:start; border-bottom:1px solid var(--color-rule, #d5d7cf); padding:10px 0; animation:ds-slip-in 220ms cubic-bezier(0.16, 1, 0.3, 1) both; } .ds-slip-name { font-size:0.875rem; line-height:1.25rem; font-weight:500; color:var(--color-ink, #1c1f22); } .ds-slip-note { margin:2px 0 0; font-size:0.75rem; line-height:1rem; color:var(--color-ink-2, #4a4f54); } .ds-slip-bar { margin-top:6px; height:4px; background:var(--color-office, #2f5d4a); } .ds-bar-stamp { background:var(--color-stamp, #4536a8); } .ds-slip-sign { margin-top:2px; display:inline-flex; height:24px; align-items:center; justify-content:center; transform:rotate(-2deg); border:1.5px solid; font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:0.75rem; font-weight:700; letter-spacing:0.05em; } .ds-tone-office { border-color:var(--color-office, #2f5d4a); color:var(--color-office, #2f5d4a); } .ds-tone-stamp { border-color:var(--color-stamp, #4536a8); color:var(--color-stamp, #4536a8); } .ds-tone-tab { border-color:var(--color-tab-ink, #5a4708); background:var(--color-tab, #e7c43f); color:var(--color-tab-ink, #5a4708); } .ds-slip-took { margin-top:2px; text-align:right; font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.75rem; line-height:1rem; color:var(--color-ink-2, #4a4f54); } @keyframes ds-slip-in { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:none; } } @media (prefers-reduced-motion: reduce) { .ds-slip-row { animation:none; } }" + }, + { + "name": "Fact Row with provenance", + "kind": "custom", + "refersTo": "fact-row", + "description": "A quoted fact: BT code, label, value, provenance chip. Hover/focus paints stamp wash and highlights the quote on the page; a missing fact sits on tab wash.", + "html": "
BT-1Invoice numberR123456789p.1
BT-112Total21.701,70 €calc
BT-84IBANNot on the document
", + "css": ".ds-facts { margin:0; border-top:1px solid var(--color-rule-strong, #b6b9ae); font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; line-height:1.25rem; } .ds-fact { display:grid; grid-template-columns:4.25rem minmax(0,9rem) 1fr 2.25rem; column-gap:12px; align-items:baseline; border-bottom:1px solid var(--color-rule, #d5d7cf); padding:6px 0; outline:none; } .ds-fact[tabindex]:hover, .ds-fact[tabindex]:focus-visible { background:var(--color-stamp-wash, #e7e4f6); } .ds-fact-bt { font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.75rem; color:var(--color-ink-3, #62676d); } .ds-fact-label { color:var(--color-ink-2, #4a4f54); } .ds-fact-value { color:var(--color-ink, #1c1f22); } .ds-chip { text-align:right; font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.75rem; color:var(--color-ink-2, #4a4f54); } .ds-chip-calc { color:var(--color-office, #2f5d4a); } .ds-fact-missing { background:var(--color-tab-wash, #f7eec4); } .ds-fact-missing .ds-fact-value { color:var(--color-tab-ink, #5a4708); font-weight:500; }" + }, + { + "name": "Stamp Button (primary)", + "kind": "button", + "refersTo": "button-stamp", + "description": "The result download that exists only when the validator accepted; violet fill, turns ink on hover.", + "html": "", + "css": ".ds-btn-stamp { display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 12px; border:0; border-radius:0; background:var(--color-stamp, #4536a8); color:#fff; font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; font-weight:600; cursor:pointer; transition:background-color 150ms cubic-bezier(0.4,0,0.2,1); } .ds-btn-stamp:hover, .ds-btn-stamp:focus-visible { background:var(--color-ink, #1c1f22); } .ds-btn-stamp:focus-visible { outline:2px solid var(--color-stamp, #4536a8); outline-offset:2px; }" + }, + { + "name": "Outline Button (secondary)", + "kind": "button", + "refersTo": "button-outline", + "description": "Secondary result action (Audit log); 1px ink border, inverts to ink on hover and focus.", + "html": "", + "css": ".ds-btn-outline { display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 12px; border:1px solid var(--color-ink, #1c1f22); border-radius:0; background:transparent; color:var(--color-ink, #1c1f22); font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; font-weight:600; cursor:pointer; } .ds-btn-outline:hover, .ds-btn-outline:focus-visible { background:var(--color-ink, #1c1f22); color:var(--color-sheet, #fbfbf8); } .ds-btn-outline:focus-visible { outline:2px solid var(--color-stamp, #4536a8); outline-offset:2px; }" + }, + { + "name": "Ink Button (form action)", + "kind": "button", + "refersTo": "button-ink", + "description": "Form actions such as Choose a file and Validate again; ink fill, turns violet on hover, ink-3 when disabled.", + "html": " ", + "css": ".ds-btn-ink { height:40px; padding:0 16px; border:0; border-radius:0; background:var(--color-ink, #1c1f22); color:var(--color-sheet, #fbfbf8); font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; font-weight:600; cursor:pointer; transition:background-color 150ms cubic-bezier(0.4,0,0.2,1); } .ds-btn-ink:hover, .ds-btn-ink:focus-visible { background:var(--color-stamp, #4536a8); } .ds-btn-ink:focus-visible { outline:2px solid var(--color-stamp, #4536a8); outline-offset:2px; } .ds-btn-ink:disabled { background:var(--color-ink-3, #62676d); cursor:not-allowed; }" + }, + { + "name": "Needs-Input Field", + "kind": "input", + "refersTo": "input-field", + "description": "The short, specific question: tab-wash panel, labelled mono field with BT code, violet focus ring.", + "html": "

One fact is required for an XRechnung but not printed on the document. Verdict will not invent it.

", + "css": ".ds-ask { background:var(--color-tab-wash, #f7eec4); padding:16px; font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); } .ds-ask-copy { margin:0; font-size:0.875rem; line-height:1.25rem; color:var(--color-tab-ink, #5a4708); } .ds-ask-label { margin-top:12px; display:flex; flex-direction:column; gap:4px; font-size:0.875rem; font-weight:500; color:var(--color-ink, #1c1f22); } .ds-ask-bt { font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.75rem; font-weight:400; color:var(--color-ink-3, #62676d); } .ds-ask-input { height:40px; padding:0 12px; border:1px solid rgba(90,71,8,0.4); border-radius:0; background:var(--color-sheet, #fbfbf8); color:var(--color-ink, #1c1f22); font-family:var(--font-mono, \"JetBrains Mono\", ui-monospace, \"SF Mono\", monospace); font-size:0.875rem; outline:none; caret-color:var(--color-stamp, #4536a8); } .ds-ask-input:focus { border-color:var(--color-stamp, #4536a8); box-shadow:0 0 0 2px rgba(69,54,168,0.25); }" + }, + { + "name": "Work-Area Tabs", + "kind": "nav", + "refersTo": "work-area-tab", + "description": "Below 1024px: one desk at a time; active tab in ink with a 2px violet underline.", + "html": "", + "css": ".ds-tabs { display:flex; background:var(--color-sheet, #fbfbf8); border-bottom:1px solid var(--color-rule, #d5d7cf); } .ds-tab { flex:1; padding:10px 0; border:0; border-bottom:2px solid transparent; background:transparent; font-family:var(--font-sans, \"Barlow\", ui-sans-serif, system-ui, sans-serif); font-size:0.875rem; font-weight:500; color:var(--color-ink-2, #4a4f54); cursor:pointer; } .ds-tab[aria-selected=\"true\"] { border-bottom-color:var(--color-stamp, #4536a8); color:var(--color-ink, #1c1f22); } .ds-tab:hover { color:var(--color-ink, #1c1f22); } .ds-tab:focus-visible { outline:2px solid var(--color-stamp, #4536a8); outline-offset:2px; }" + }, + { + "name": "Outcome Tags", + "kind": "chip", + "refersTo": "outcome-tag-asks", + "description": "Small square stamps on recorded runs: verified (violet), rejected (red), asks (yellow fill, tab-ink text).", + "html": "verified asks · verified rejected", + "css": ".ds-tag { display:inline-block; border:1.5px solid; padding:0 6px; font-family:var(--font-stamp, \"Barlow Condensed\", \"Barlow\", ui-sans-serif, sans-serif); font-size:0.75rem; line-height:1rem; font-weight:700; letter-spacing:0.12em; text-transform:uppercase; } .ds-tag-verified { border-color:var(--color-stamp, #4536a8); color:var(--color-stamp, #4536a8); } .ds-tag-rejected { border-color:var(--color-void, #c2382b); color:var(--color-void, #c2382b); } .ds-tag-asks { border-color:var(--color-tab-ink, #5a4708); background:var(--color-tab, #e7c43f); color:var(--color-tab-ink, #5a4708); }" + } + ], + "narrative": { + "northStar": "The Routing Slip and the Inspection Stamp", + "overview": "Verdict is a German office form (Behörden-Laufzettel) put on screen. An invoice travels from desk to desk; every station signs a ruled slip with its initials and the time it took, and the outcome is pressed onto the sheet as an inspection stamp carrying the validator version, the date and the report hash. The interface is a working surface, not a dashboard: recycled-paper grey ground, near-black ink, hairline rules, square corners, and a small set of stamp inks that each mean one thing.\n\nDensity is that of a filled-in form. Most text runs at 14px in a DIN-like grotesk (Barlow); machine readouts (BT codes, hashes, milliseconds, cents) run at 12px in JetBrains Mono; and exactly one large figure per result, the computed total, sits next to the stamp. Colour is flat and committed. The one physical object on the desk, the printed invoice page, is the only thing lifted with a shadow; it stays registered in place across every state while marks appear on it.\n\nThe system refuses the category default of \"PDF left, field list right, green ticks\". Validity is never a check icon; it is a stamp. Refused values are never hidden; they are struck through and cancelled with a VOID mark.", + "keyCharacteristics": [ + "Recycled-paper ground, near-black ink, flat colour, no gradients, no glass.", + "Square corners everywhere; hierarchy comes from rule weight, not boxes.", + "Four inks with fixed meanings: stamp violet, void red, folder-tab yellow, office green.", + "Condensed caps for anything a rubber stamp or a printed form label would carry.", + "Monospace for every machine readout; one large proportional figure per result.", + "Bar length on the routing slip equals real duration.", + "Motion is a press and a slide, never a bounce; both switch off under reduced motion." + ], + "rules": [ + { + "name": "The One Ink, One Meaning Rule", + "body": "Violet means accepted or in focus, red means refused, yellow means waiting for you, green means the shell and the model, ink-grey means deterministic core. A colour never changes meaning between components.", + "section": "colors" + }, + { + "name": "The Wash Is Not Ink Rule", + "body": "Washes (`stamp-wash`, `void-wash`, `tab-wash`) are backgrounds only. Text on a wash uses the matching full ink.", + "section": "colors" + }, + { + "name": "The Rubber Stamp Rule", + "body": "Condensed uppercase with open tracking is reserved for text a stamp or a printed form label would carry: the verdict stamp, VOID marks, station initials, group and column headings, the wordmark. Sentences are never set in it.", + "section": "typography" + }, + { + "name": "The Machine Readout Rule", + "body": "Anything a machine produced or a standard defines (BT-xx, sha256 fragments, ms, $ cost, unit codes, p.N) is mono at 12px. Prose and the large total are never mono.", + "section": "typography" + }, + { + "name": "The Registered Page Rule", + "body": "The rendered invoice page never moves or reflows between states. Only marks change on it: quote highlights for every quoted fact, a violet outlined box for the fact in focus.", + "section": "layout" + }, + { + "name": "The One Lifted Sheet Rule", + "body": "Only the invoice page casts a shadow. Panels, buttons, stamps and tags stay flat.", + "section": "elevation" + }, + { + "name": "The Square Corner Rule", + "body": "Radius is 0 everywhere. Softness, where needed, comes from washes, not curves.", + "section": "shapes" + } + ], + "dos": [ + "**Do** keep every corner square (0px) and draw structure with the three rule weights: 1px `rule`, 1px `rule-strong`, 2px `ink`.", + "**Do** show validity only as the VERIFIED stamp, and only with the validator version, date and `report ` line on it.", + "**Do** cancel instead of removing: strike the value with a 1.5px `void` line and set a VOID mark beside it.", + "**Do** set BT codes, hashes, durations, token counts and costs in JetBrains Mono at 12px.", + "**Do** size routing-slip bars by real duration relative to the longest station.", + "**Do** keep the invoice page registered; express state only through marks on it.", + "**Do** colour by actor and outcome per the One Ink, One Meaning Rule.", + "**Do** wrap every entrance animation in the `prefers-reduced-motion: reduce` switch." + ], + "donts": [ + "**Don't** use gradients, glass, blur effects or backdrop filters on surfaces.", + "**Don't** round corners.", + "**Don't** use green check marks, tick icons or \"success green\" for validity; green belongs to the shell and the model.", + "**Don't** hide refused values or collapse them out of view.", + "**Don't** set yellow (`tab`) as text; use `tab-ink`.", + "**Don't** cast shadows on anything but the invoice page.", + "**Don't** set sentences in condensed caps or prose in mono." + ] + } +} diff --git a/apps/web/.impeccable/surfaces/app-page-tsx.md b/apps/web/.impeccable/surfaces/app-page-tsx.md new file mode 100644 index 0000000..5b93d53 --- /dev/null +++ b/apps/web/.impeccable/surfaces/app-page-tsx.md @@ -0,0 +1,24 @@ +--- +version: 1 +slug: "app-page-tsx" +primary_target: "app/page.tsx" +related_targets: [] +--- + +# Surface: Verdict app (single screen, `/`) + +Mode: Operate. Audience: hackathon jury via a ≤ 3-minute video; demo persona a German small business with PDF invoices. Task: drop a PDF (or pick a gallery example), watch the run, read the verdict, answer missing facts, download XRechnung + audit log. Constraints: English UI, invoice content German, Nebius Token Factory and Nemotron named visibly, gallery works with no budget, desktop first for the video, usable on mobile. + +## Direction contract + +THESIS: The run is a German office routing slip (Laufzettel): every station signs off with time and initials, and the verdict lands as an official inspection stamp with the report hash. Refuses the category default of "PDF left, field list right, green ticks". + +OWN-WORLD: Recycled-paper grey ground (#F2F3EE), ink near-black, violet stamp ink (#4536A8) for accepted, stamp red (#C2382B) for rejected, folder-tab yellow (#E7C43F) for needs-input, office green (#2F5D4A) for the shell rail. Flat colour only, no gradients or glass. DIN-like grotesk (Barlow / Barlow Condensed for stamp caps) plus a monospace for BT codes, hashes, ms and cents. Hairline rules, square corners. + +STORY: The visitor sees an invoice go in, watches Nemotron and the validator sign off station by station, understands every value is quoted from the page, sees rejected values struck through and stamped VOID instead of hidden, and leaves with a stamped, valid e-invoice or a short, specific question. + +FIRST VIEWPORT: Top bar (Verdict wordmark, "Nemotron on Nebius Token Factory · KoSIT 1.6.3", budget meter, gallery). Three work areas: left the PDF page with quote highlights; centre the extracted facts grouped (Invoice, Seller, Buyer, Payment, Lines, Totals), each with BT code and provenance; right the routing slip as a vertical timeline whose bar lengths are real durations. The verdict stamp sits at the top of the centre column, rotated about -4°, the signature move. + +FORM: Behörden-Laufzettel & Prüfstempel, candidate 3 of 7 on the ordered list; seed key b067d9c4. Raises: nothing disappears, it cancels (ticket wallet); length = duration (Labanotation); registered page across states (botanical folio); flat committed colour (zoo map); tiny monospace readouts against one large figure (type specimen). + +FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md new file mode 100644 index 0000000..643577d --- /dev/null +++ b/apps/web/AGENTS.md @@ -0,0 +1,9 @@ + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/apps/web/CLAUDE.md b/apps/web/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/apps/web/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/apps/web/DESIGN.md b/apps/web/DESIGN.md new file mode 100644 index 0000000..e6c3de8 --- /dev/null +++ b/apps/web/DESIGN.md @@ -0,0 +1,409 @@ +--- +name: Verdict +description: PDF invoice to valid XRechnung, recorded as a German office routing slip and an inspection stamp. +colors: + paper: "#f2f3ee" + sheet: "#fbfbf8" + page-white: "#ffffff" + rule: "#d5d7cf" + rule-strong: "#b6b9ae" + ink: "#1c1f22" + ink-2: "#4a4f54" + ink-3: "#62676d" + office: "#2f5d4a" + office-2: "#3c6f59" + office-ink: "#e8efe9" + stamp: "#4536a8" + stamp-wash: "#e7e4f6" + void: "#c2382b" + void-wash: "#f6e3df" + tab: "#e7c43f" + tab-wash: "#f7eec4" + tab-ink: "#5a4708" + quote: "#f3e7a0" +typography: + stamp-title: + fontFamily: "Barlow Condensed, Barlow, ui-sans-serif, sans-serif" + fontSize: "2.75rem" + fontWeight: 700 + lineHeight: 1 + letterSpacing: "0.08em" + display: + fontFamily: "Barlow, ui-sans-serif, system-ui, sans-serif" + fontSize: "2.5rem" + fontWeight: 600 + lineHeight: 1.05 + letterSpacing: "-0.025em" + figure: + fontFamily: "Barlow, ui-sans-serif, system-ui, sans-serif" + fontSize: "2.5rem" + fontWeight: 600 + lineHeight: 1.25 + letterSpacing: "-0.025em" + fontFeature: "\"tnum\" 1, \"lnum\" 1" + wordmark: + fontFamily: "Barlow Condensed, Barlow, ui-sans-serif, sans-serif" + fontSize: "1.5rem" + fontWeight: 700 + lineHeight: 1 + letterSpacing: "0.06em" + title-caps: + fontFamily: "Barlow Condensed, Barlow, ui-sans-serif, sans-serif" + fontSize: "0.875rem" + fontWeight: 600 + lineHeight: 1.25rem + letterSpacing: "0.12em" + body-lg: + fontFamily: "Barlow, ui-sans-serif, system-ui, sans-serif" + fontSize: "1rem" + fontWeight: 400 + lineHeight: 1.5rem + body: + fontFamily: "Barlow, ui-sans-serif, system-ui, sans-serif" + fontSize: "0.875rem" + fontWeight: 400 + lineHeight: 1.25rem + fontFeature: "\"tnum\" 1, \"lnum\" 1" + label-caps: + fontFamily: "Barlow Condensed, Barlow, ui-sans-serif, sans-serif" + fontSize: "0.75rem" + fontWeight: 600 + lineHeight: 1rem + letterSpacing: "0.1em" + note: + fontFamily: "Barlow, ui-sans-serif, system-ui, sans-serif" + fontSize: "0.75rem" + fontWeight: 400 + lineHeight: 1rem + mono: + fontFamily: "JetBrains Mono, ui-monospace, SF Mono, monospace" + fontSize: "0.75rem" + fontWeight: 400 + lineHeight: 1rem +rounded: + none: "0px" +spacing: + row-tight: "6px" + row: "10px" + inline: "8px" + cell: "12px" + panel: "16px" + gutter-mobile: "16px" + gutter-desktop: "24px" + stack: "24px" + column-gap: "32px" + group: "32px" +components: + top-bar: + backgroundColor: "{colors.office}" + textColor: "{colors.office-ink}" + height: "56px" + padding: "0 24px" + button-stamp: + backgroundColor: "{colors.stamp}" + textColor: "{colors.page-white}" + typography: "{typography.body}" + rounded: "{rounded.none}" + height: "36px" + padding: "0 12px" + button-stamp-hover: + backgroundColor: "{colors.ink}" + textColor: "{colors.page-white}" + button-outline: + backgroundColor: "transparent" + textColor: "{colors.ink}" + rounded: "{rounded.none}" + height: "36px" + padding: "0 12px" + button-outline-hover: + backgroundColor: "{colors.ink}" + textColor: "{colors.sheet}" + button-ink: + backgroundColor: "{colors.ink}" + textColor: "{colors.sheet}" + typography: "{typography.body}" + rounded: "{rounded.none}" + height: "40px" + padding: "0 16px" + button-ink-hover: + backgroundColor: "{colors.stamp}" + textColor: "{colors.sheet}" + button-ink-disabled: + backgroundColor: "{colors.ink-3}" + textColor: "{colors.sheet}" + verdict-stamp-verified: + backgroundColor: "transparent" + textColor: "{colors.stamp}" + typography: "{typography.stamp-title}" + rounded: "{rounded.none}" + padding: "10px 20px" + verdict-stamp-rejected: + backgroundColor: "transparent" + textColor: "{colors.void}" + typography: "{typography.stamp-title}" + rounded: "{rounded.none}" + padding: "10px 20px" + verdict-stamp-returned: + backgroundColor: "transparent" + textColor: "{colors.tab-ink}" + typography: "{typography.stamp-title}" + rounded: "{rounded.none}" + padding: "10px 20px" + void-mark: + backgroundColor: "transparent" + textColor: "{colors.void}" + typography: "{typography.label-caps}" + rounded: "{rounded.none}" + padding: "0 6px" + outcome-tag-asks: + backgroundColor: "{colors.tab}" + textColor: "{colors.tab-ink}" + typography: "{typography.label-caps}" + rounded: "{rounded.none}" + padding: "0 6px" + outcome-tag-verified: + backgroundColor: "transparent" + textColor: "{colors.stamp}" + typography: "{typography.label-caps}" + padding: "0 6px" + slip-row: + backgroundColor: "transparent" + textColor: "{colors.ink}" + typography: "{typography.body}" + padding: "10px 0" + slip-initials-signed: + backgroundColor: "transparent" + textColor: "{colors.ink-2}" + typography: "{typography.label-caps}" + rounded: "{rounded.none}" + height: "24px" + width: "60px" + slip-initials-returned: + backgroundColor: "{colors.tab}" + textColor: "{colors.tab-ink}" + height: "24px" + width: "60px" + slip-initials-pending: + backgroundColor: "transparent" + textColor: "{colors.ink-3}" + height: "24px" + width: "60px" + fact-row: + backgroundColor: "transparent" + textColor: "{colors.ink}" + typography: "{typography.body}" + padding: "6px 0" + fact-row-hover: + backgroundColor: "{colors.stamp-wash}" + fact-row-missing: + backgroundColor: "{colors.tab-wash}" + textColor: "{colors.tab-ink}" + provenance-chip: + textColor: "{colors.ink-2}" + typography: "{typography.mono}" + needs-input-panel: + backgroundColor: "{colors.tab-wash}" + textColor: "{colors.tab-ink}" + rounded: "{rounded.none}" + padding: "16px" + input-field: + backgroundColor: "{colors.sheet}" + textColor: "{colors.ink}" + typography: "{typography.mono}" + rounded: "{rounded.none}" + height: "40px" + padding: "0 12px" + work-area-tab: + backgroundColor: "{colors.sheet}" + textColor: "{colors.ink-2}" + typography: "{typography.body}" + padding: "10px 0" + work-area-tab-active: + textColor: "{colors.ink}" + drop-zone: + backgroundColor: "{colors.sheet}" + textColor: "{colors.ink}" + rounded: "{rounded.none}" + padding: "40px 24px" + drop-zone-active: + backgroundColor: "{colors.stamp-wash}" +--- + +# Design System: Verdict + +## Overview + +**Creative North Star: "The Routing Slip and the Inspection Stamp"** + +Verdict is a German office form (Behörden-Laufzettel) put on screen. An invoice travels from desk to desk; every station signs a ruled slip with its initials and the time it took, and the outcome is pressed onto the sheet as an inspection stamp carrying the validator version, the date and the report hash. The interface is a working surface, not a dashboard: recycled-paper grey ground, near-black ink, hairline rules, square corners, and a small set of stamp inks that each mean one thing. + +Density is that of a filled-in form. Most text runs at 14px in a DIN-like grotesk (Barlow); machine readouts (BT codes, hashes, milliseconds, cents) run at 12px in JetBrains Mono; and exactly one large figure per result, the computed total, sits next to the stamp. Colour is flat and committed. The one physical object on the desk, the printed invoice page, is the only thing lifted with a shadow; it stays registered in place across every state while marks appear on it. + +The system refuses the category default of "PDF left, field list right, green ticks". Validity is never a check icon; it is a stamp. Refused values are never hidden; they are struck through and cancelled with a VOID mark. + +**Key Characteristics:** +- Recycled-paper ground, near-black ink, flat colour, no gradients, no glass. +- Square corners everywhere; hierarchy comes from rule weight, not boxes. +- Four inks with fixed meanings: stamp violet, void red, folder-tab yellow, office green. +- Condensed caps for anything a rubber stamp or a printed form label would carry. +- Monospace for every machine readout; one large proportional figure per result. +- Bar length on the routing slip equals real duration. +- Motion is a press and a slide, never a bounce; both switch off under reduced motion. + +## Colors + +A grey paper ground with near-black ink and four committed stamp inks, each owned by one actor or outcome. + +### Primary +- **Stamp Violet** (`stamp`): the accepting ink. The VERIFIED stamp, the KoSIT initials when the validator accepts, the primary download button, the focused-quote outline on the PDF page, the focus ring, the active mobile tab underline, text selection, input caret. +- **Stamp Wash** (`stamp-wash`): the violet tint for hover and focus backgrounds on fact rows, line-item rows, recorded-run rows, and the drop zone while a file is dragged over it. + +### Secondary +- **Void Red** (`void`): the cancelling ink. The REJECTED stamp, VOID marks, strike-through decoration on refused values and on a disagreeing printed total, rule IDs of validator errors, the KoSIT initials and duration bar when validation fails, error and budget-exhausted messages. +- **Void Wash** (`void-wash`): background of the single struck word inside a refused value. + +### Tertiary +- **Folder-Tab Yellow** (`tab`): the "needs you" marker as a fill: the RTN and YOU initials, the "asks" outcome tag, their duration bars. +- **Tab Wash** (`tab-wash`): the needs-input panel, missing fact rows, the "second pass" divider on the routing slip. +- **Tab Ink** (`tab-ink`): text and borders in the needs-input world, including the RETURNED stamp. Yellow itself is never used as text. +- **Office Green** (`office`): the shell rail (top bar) and the model actor: Nemotron initials, their duration bars, and the `calc` provenance chip. +- **Office Green Light** (`office-2`): the hairline under the mobile tagline strip inside the green rail. +- **Office Ink** (`office-ink`): text on the green rail. + +### Neutral +- **Recycled Paper** (`paper`): the page ground. +- **Form Sheet** (`sheet`): raised form surfaces: drop zone, input fields, mobile tab bar; also the text colour on ink buttons. +- **Page White** (`page-white`): the rendered invoice page and text on violet buttons and the wordmark. +- **Hairline** (`rule`): row dividers in every list and table; skeleton bars. +- **Strong Hairline** (`rule-strong`): group top rules, column-header rules, the dashed pending-initials boxes, the idle drop-zone border. +- **Ink** (`ink`): primary text, the 2px structural rules, the ink button. +- **Ink 2** (`ink-2`): secondary text, labels, station notes, the deterministic-core initials and bars. +- **Ink 3** (`ink-3`): tertiary metadata: BT codes, column headers, file reference, placeholders, idle stations. +- **Quote Highlighter** (`quote`): the faint yellow highlight laid (at 30%, multiplied) over every quoted span on the PDF page. + +### Named Rules +**The One Ink, One Meaning Rule.** Violet means accepted or in focus, red means refused, yellow means waiting for you, green means the shell and the model, ink-grey means deterministic core. A colour never changes meaning between components. + +**The Wash Is Not Ink Rule.** Washes (`stamp-wash`, `void-wash`, `tab-wash`) are backgrounds only. Text on a wash uses the matching full ink. + +## Typography + +**Display Font:** Barlow (with ui-sans-serif, system-ui) +**Stamp Font:** Barlow Condensed (with Barlow) +**Mono Font:** JetBrains Mono (with ui-monospace, SF Mono) + +**Character:** A DIN-like grotesk for the form, its condensed cut for what a rubber stamp or pre-printed label would say, and a monospace for what a machine printed. Tabular, lining figures are on globally (`font-feature-settings: "tnum" 1, "lnum" 1`). + +### Hierarchy +- **Stamp Title** (Barlow Condensed 700, 2.75rem; 2.25rem below 640px; line-height 1; 0.08em; uppercase): VERIFIED / REJECTED / RETURNED inside the verdict stamp. +- **Display** (Barlow 600, 2.5rem; 2rem below 640px; line-height 1.05; -0.025em): the single idle-state headline. +- **Figure** (Barlow 600, 2.5rem, line-height 1.25, -0.025em): the computed BT-112 total next to the stamp. One per result. +- **Wordmark** (Barlow Condensed 700, 1.5rem, 0.06em, uppercase): "VERDICT" in the top bar. +- **Title Caps** (Barlow Condensed 600, 0.875rem, 0.12em, uppercase): fact group headings (INVOICE, SELLER, …), "Recorded runs", "Refused, not hidden" (in void), Principles terms. The "Routing slip" heading uses the same cut at 1rem, weight 700. +- **Body** (Barlow 400/500, 0.875rem / 1.25rem): fact labels and values, station names (500), explanations held to 44–58ch. +- **Body Large** (Barlow 400/500, 1rem / 1.5rem): idle explainer paragraph, drop-zone prompt. +- **Label Caps** (Barlow Condensed 600–700, 0.75rem, 0.1–0.14em, uppercase): slip column headers, stamp sub-lines (0.14em), station initials, VOID marks and outcome tags (700, 0.12em). +- **Note** (Barlow 400, 0.75rem / 1rem): station notes, validator wording, recorded-run stories. +- **Mono** (JetBrains Mono 400, 0.75rem / 1rem): BT codes, hashes, durations, token counts, costs, unit codes, provenance chips. At 0.875rem it sets the totals ledger and the needs-input field. + +### Named Rules +**The Rubber Stamp Rule.** Condensed uppercase with open tracking is reserved for text a stamp or a printed form label would carry: the verdict stamp, VOID marks, station initials, group and column headings, the wordmark. Sentences are never set in it. + +**The Machine Readout Rule.** Anything a machine produced or a standard defines (BT-xx, sha256 fragments, ms, $ cost, unit codes, p.N) is mono at 12px. Prose and the large total are never mono. + +## Layout + +A three-desk layout inside a 1600px max container with 16px gutters (24px from 768px). From 1024px the main grid is three columns `minmax(0,1fr) | minmax(0,1.15fr) | minmax(16rem,0.75fr)` with a 32px gap (24px below): the PDF page on the left, the stamp and facts in the wider centre, the routing slip on the right. The left and right columns are sticky 24px from the top so the page and the slip stay in view while the facts scroll. + +Below 1024px, once a run exists, a three-tab bar (Document / Facts / Routing slip) shows one desk at a time, Facts first. In the idle state the explainer moves above the intake on small screens and the principles list follows the intake. + +The result head is a wrapping row (32px column gap, 16px row gap, minimum height 9.5rem) holding stamp, total and actions, closed by a 1px strong rule. Fact groups stack 32px apart; result blocks stack 24px apart. List rhythm is tight and form-like: 6px vertical padding on fact rows, 10px on slip rows and recorded runs. + +### Named Rules +**The Registered Page Rule.** The rendered invoice page never moves or reflows between states. Only marks change on it: quote highlights for every quoted fact, a violet outlined box for the fact in focus. + +## Elevation & Depth + +Flat by default; depth comes from rule weight and the paper/sheet tone step. There is exactly one shadow: the rendered invoice page, which sits on the desk as a physical sheet (`0 1px 2px rgba(28,31,34,0.08), 0 8px 24px -12px rgba(28,31,34,0.18)`). Stamps and quote highlights use `mix-blend-mode: multiply`, so ink sits in the paper rather than on top of it. + +### Named Rules +**The One Lifted Sheet Rule.** Only the invoice page casts a shadow. Panels, buttons, stamps and tags stay flat. + +## Shapes + +Square corners throughout (0px radius); nothing in the build is rounded. Structure is drawn with rules in three weights: a 1px hairline (`rule`) between rows, a 1px strong hairline (`rule-strong`) above groups and under column headers, and a 2px ink rule for the routing-slip head and the totals ledger. Dashed rules mark something not yet filled in: the 2px dashed drop zone, 1px dashed pending initials boxes, and the 2px dashed tab-ink divider where a resumed run continues on the same slip. + +Stamp geometry is a double rule: a 3px outer border, a 3px gap, a 1.5px inner border. Small stamps (initials, VOID marks, outcome tags) use a single 1.5px border. Stamps sit slightly off-axis: the verdict stamp at -4° (verified), +3° (rejected), -3° (returned); VOID marks at -3°; signed initials at -2°. + +### Named Rules +**The Square Corner Rule.** Radius is 0 everywhere. Softness, where needed, comes from washes, not curves. + +## Components + +### Buttons +Flat, square, short, and decisive. +- **Shape:** square corners (0px), 600 weight, 14px label, optional 14px Lucide icon at stroke 2 with a 6px gap. +- **Stamp (primary result action):** violet fill, white text, 36px tall, 12px side padding. Used for the one download that only exists when the validator accepted (XRechnung). +- **Outline (secondary result action):** 1px ink border, ink text, same size; hover and focus invert to ink fill with sheet text. Used for Audit log. +- **Ink (form action):** ink fill, sheet text, 40px tall, 16px side padding; hover and focus turn violet. Disabled turns `ink-3` with a not-allowed cursor. Used for "Choose a file" and "Validate again". +- **Hover / Focus:** colour swap only (150ms colour transition); focus-visible also gets the global 2px violet outline at 2px offset. +- **Text link button:** "← New invoice", 14px `ink-2` turning `ink` on hover, 14px arrow icon at stroke 1.75. + +### Chips (provenance and outcome) +- **Provenance chip:** a mono 12px readout at the row end, no box. `p.N` in `ink-2` for a quote from page N, `you` in `tab-ink` for a user answer, `calc` in `office` for a computed value (rule name in its title), `—` in `ink-3` when there is none. +- **Outcome tag:** a small square stamp (1.5px border, 6px side padding, 16px line, Barlow Condensed 700 12px, 0.12em, uppercase). "verified" in violet, "rejected" in red, "asks" and "asks · verified" filled yellow with tab-ink text. + +### Verdict Stamp (signature component) +The result is pressed onto the sheet at the top of the centre column. Double-ruled frame (3px outer, 1.5px inner), stamp title, one or two label-caps lines (validator and XRechnung version, then the date of the VALIDATE event; or "Input needed" and the open-point count; or "See rule violations"), and for VERIFIED only a mono `report ` line. Ink colour by outcome: violet, red, or tab-ink. Rotation as listed under Shapes. Enters with the stamp-press motion. While running, the slot shows "On its way through the office…" in condensed caps, `ink-3`. + +### Void Mark +"Nothing disappears; it cancels." A small square stamp reading VOID (or "Not used" beside a printed total that disagrees with the computed one): 1.5px red border, red Barlow Condensed 700 12px caps, -3° rotation. It always accompanies a strike-through (`line-through`, red, 1.5px decoration) on the cancelled value. In the "Refused, not hidden" list only the first word that is not on the page is struck and marked with `void-wash`; the surrounding words stay readable, and a note says what the page prints instead. + +### Routing Slip +A ruled form in the right column. Head: "ROUTING SLIP" in condensed caps over a 2px ink rule, with stamp count and total duration, and the file reference (first 16 hex of the upload hash, mono). Column header row: Station / Signed / Took in label caps, 1px strong rule. Each row is a three-column grid (`1fr | 3.75rem | 4.25rem`, 12px gap, 10px vertical padding, hairline below): +- **Station:** name in 14px/500, a 12px note from the audit event (token counts, quote counts, hashes), and a 4px duration bar whose width is the station's share of the longest station (minimum 1.5%). +- **Signed:** the station initials in a 24px-high, 1.5px-bordered, -2° rotated box, coloured by tone: office green for model stations, ink-2 for deterministic core, violet or red for the validator outcome, yellow fill for return and user input, red for a line-mismatch cross-check. +- **Took:** duration in mono, right-aligned (`ms` below one second, `s` with two decimals above, `–` for none). +- **Idle:** the eight stations of a clean run in `ink-3` with dashed initials boxes. +- **Running:** a trailing "At the next desk" row with a pulsing dashed box. +- **Second pass:** a resumed run continues below a dashed tab-ink divider on a tab wash reading "RETURNED · ANSWERED BY YOU · SECOND PASS"; earlier rows stay. +- New rows enter with the slip-in motion. + +### Fact Rows +A definition list per group (Invoice, Seller, Buyer, Payment), each group under title caps and a strong hairline. Row grid: BT code (mono 12px `ink-3`, 4.25rem) | label (`ink-2`, up to 9rem) | value (`ink`) | provenance chip (right-aligned, 2.25rem). Below 640px the label drops above the value at 12px. Rows with a quote are focusable; hover or focus paints `stamp-wash` and draws the violet focus box over the quote on the PDF page. Missing facts get a `tab-wash` row with "Not on the document" in tab-ink 500. Line items use a table with BT-numbered mono column headers; the totals ledger closes the facts under a 2px ink rule, mono, with BT-112 in ink 600. + +### Inputs / Fields (needs-input form) +- **Panel:** `tab-wash` block, 16px padding, square, tab-ink copy stating how many facts are required and that Verdict will not invent them. +- **Field:** label in 14px/500 ink with the BT code in mono `ink-3`; input 40px tall, 1px `tab-ink` border at 40%, `sheet` fill, mono 14px value, 12px side padding. +- **Focus:** border turns violet plus a 2px violet ring at 25%; caret is violet. +- **Action:** ink button "Validate again". +- **Contradiction variant:** the same wash panel with a bulleted list of open issues and no fields. + +### Navigation +- **Top bar:** office-green rail, 56px tall: condensed wordmark (also the start-over button), a tagline in office-ink at 80% from 1024px, and on the right the model/validator credit line (from 768px) and the mono budget readout (`$spent / $budget today`, or "live runs paused"). Below 768px the credit moves into a strip under the rail, closed by an `office-2` hairline. +- **Work-area tabs (below 1024px):** three equal tabs on `sheet` over a hairline; 14px/500; inactive `ink-2`; active `ink` with a 2px violet bottom border. + +### Intake +- **Drop zone:** at least 16rem tall, centred, 2px dashed `rule-strong` border on `sheet`; while dragging, the border turns violet over `stamp-wash`. A 28px file icon (stroke 1.5), a 16px/500 prompt, a 14px constraint line (26ch), the ink button. When live runs are paused the zone drops to 60% opacity and says so in red. +- **Recorded runs:** a ruled list of buttons (title 14px/500, story 12px) with an outcome tag on the right; hover and focus paint `stamp-wash`. + +## Do's and Don'ts + +### Do: +- **Do** keep every corner square (0px) and draw structure with the three rule weights: 1px `rule`, 1px `rule-strong`, 2px `ink`. +- **Do** show validity only as the VERIFIED stamp, and only with the validator version, date and `report ` line on it. +- **Do** cancel instead of removing: strike the value with a 1.5px `void` line and set a VOID mark beside it. +- **Do** set BT codes, hashes, durations, token counts and costs in JetBrains Mono at 12px. +- **Do** size routing-slip bars by real duration relative to the longest station. +- **Do** keep the invoice page registered; express state only through marks on it. +- **Do** colour by actor and outcome per the One Ink, One Meaning Rule. +- **Do** wrap every entrance animation in the `prefers-reduced-motion: reduce` switch. + +### Don't: +- **Don't** use gradients, glass, blur effects or backdrop filters on surfaces. +- **Don't** round corners. +- **Don't** use green check marks, tick icons or "success green" for validity; green belongs to the shell and the model. +- **Don't** hide refused values or collapse them out of view. +- **Don't** set yellow (`tab`) as text; use `tab-ink`. +- **Don't** cast shadows on anything but the invoice page. +- **Don't** set sentences in condensed caps or prose in mono. diff --git a/apps/web/PRODUCT.md b/apps/web/PRODUCT.md new file mode 100644 index 0000000..5269019 --- /dev/null +++ b/apps/web/PRODUCT.md @@ -0,0 +1,58 @@ +# Product + + + +## Platform + +web + +## Users + +- **Primary audience: the hackathon jury, mostly through a ≤ 3-minute video** (Nebius × NVIDIA Global AI Hackathon, track "Best Apps and Agents"). Judges score Technological Implementation, Design, Potential Impact and Quality of the Idea equally and are not required to run the demo themselves. The interface has to make the mechanism legible on screen within seconds. +- **Demo persona:** small German businesses, trades and freelancers who still write invoices as PDF (Word, Excel, trade software) and now need EN 16931 e-invoices. They know nothing about XRechnung rules and should not have to. +- **Secondary:** bookkeeping and software vendors who care about a traceable conversion with an audit record. + +## Product Purpose + +Verdict turns a PDF invoice into a valid XRechnung (CII). NVIDIA Nemotron models on Nebius Token Factory read the document and propose; the official KoSIT validator decides. Facts that are not printed on the document are asked for, never invented. Success: an e-invoice is only shown as valid when the official validator confirms it and every field has a traceable origin. + +## Positioning + +Other AI converters let a language model write the invoice and hope it is right. Verdict never lets the model write XML or do arithmetic: every field carries a verbatim quote from the PDF, amounts are computed deterministically, and the government reference validator is the final gate. Verdict also catches errors the validator itself accepts (a misread quantity that still adds up, a unit like "Stück" that KoSIT tolerates, a postcode read as city) and stops instead of shipping them. + +## Operating Context + +- Input: a PDF with a text layer (no scans or photos in this build; Nemotron vision models are not available on Token Factory). +- Pipeline states streamed live: ingest → extract → evidence check → normalize → derive → consistency check → build CII → validate → up to 3 repair rounds → output or needs-input. +- Output: XRechnung XML, a hash-chained audit log (JSONL) verifiable with `pnpm audit:verify`. ZUGFeRD PDF/A-3 comes later in phase 4, only after the core UI ships. +- A gallery of precomputed runs on synthetic invoices (clean, noisy, mutated) must work even when the daily model budget is exhausted. +- Hosting target in the plan: Koyeb (web public, verifier internal). Demo must stay online until 15.12.2026. + +## Capabilities and Constraints + +- Scope: commercial invoice (380), EUR, SEPA credit transfer, no allowances/charges, no prepaid or rounding amounts. +- Limits: max 5 MB, max 5 pages; daily model budget and per-IP rate limit. +- Measured on the non-holdout corpus (02.10.2026): clean 41/45 valid with correct totals and full provenance, noisy 23/30 (rest asked for input), mutated 25/26 handled as expected; ~0.3 ct and ~7 s per invoice. +- All hackathon materials, including the UI, are in English. Invoice content stays German. +- No accounts, no database, no payment. + +## Brand Commitments + +- Name: **Verdict** (confirmed 02.10.2026). +- Own identity, independent of Patrick's personal design system. +- Must name "Nebius Token Factory" and the NVIDIA Nemotron models visibly (hackathon rule). +- Voice: precise, calm, evidence-first. No hype words. + +## Evidence on Hand + +- Real benchmark reports from `pnpm eval` (`eval/results/*.json|md`, regenerated per run). +- Synthetic invoice corpus (`eval/corpus/generated/`, built by `pnpm corpus:build` from the Apache-2.0 KoSIT test suite with fictional names). +- No customers, testimonials, press, pricing or deployment claims exist. Do not fabricate them. + +## Product Principles + +1. The validator has the last word; "valid" only appears with a verifier report hash behind it. +2. Show the evidence: every value can be traced to its quote on the page. +3. Ask, never invent: missing facts become a short, specific question. +4. Fail safe over fail silent: an uncertain invoice stops with a reason instead of passing. +5. Make the machinery visible but calm: the jury should see the agent work without noise. diff --git a/apps/web/app/api/budget/route.ts b/apps/web/app/api/budget/route.ts new file mode 100644 index 0000000..e0921cf --- /dev/null +++ b/apps/web/app/api/budget/route.ts @@ -0,0 +1,23 @@ +import { Decimal } from "decimal.js"; +import { DAILY_BUDGET_USD, engineClient } from "@/lib/engine.ts"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +/** UI-07: today's spend against the daily budget of the public demo. */ +export function GET(): Response { + let spent = new Decimal(0); + try { + spent = engineClient().ledger.spentToday(); + } catch { + // No API key configured: the live path is off, the gallery still works. + return Response.json({ live: false, spentUsd: "0", budgetUsd: DAILY_BUDGET_USD, remainingUsd: "0" }); + } + const budget = new Decimal(DAILY_BUDGET_USD); + return Response.json({ + live: spent.lt(budget), + spentUsd: spent.toFixed(4), + budgetUsd: budget.toFixed(2), + remainingUsd: Decimal.max(0, budget.minus(spent)).toFixed(4), + }); +} diff --git a/apps/web/app/api/run/route.ts b/apps/web/app/api/run/route.ts new file mode 100644 index 0000000..f4c7280 --- /dev/null +++ b/apps/web/app/api/run/route.ts @@ -0,0 +1,85 @@ +import type { RawExtraction, UserInput } from "@verdict/core"; +import { clientIp, MAX_UPLOAD_BYTES, takeRateLimit } from "@/lib/limits.ts"; +import type { RunEvent } from "@/lib/run-protocol.ts"; +import { streamRun, UserFacingError } from "@/lib/run-stream.ts"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; +export const maxDuration = 120; + +const json = (status: number, message: string, extra: Record = {}) => + new Response(JSON.stringify({ error: message }), { + status, + headers: { "Content-Type": "application/json", ...extra }, + }); + +/** + * POST multipart/form-data: `file` (PDF, required); optional `extraction` (JSON) and + * `userInputs` (JSON array of {path, value}) to resume a NEEDS_INPUT run without a new model call. + * Responds with a text/event-stream of RunEvents. + */ +export async function POST(request: Request): Promise { + const limit = takeRateLimit(clientIp(request.headers)); + if (!limit.ok) { + return json(429, "Too many runs from your network. Please try again later or use the gallery.", { + "Retry-After": String(limit.retryAfterS), + }); + } + + let form: FormData; + try { + form = await request.formData(); + } catch { + return json(400, "Expected a multipart upload with a PDF file."); + } + const file = form.get("file"); + if (!(file instanceof File)) return json(400, "No file uploaded."); + if (file.size > MAX_UPLOAD_BYTES) { + return json(413, `The file is larger than ${Math.round(MAX_UPLOAD_BYTES / 1024 / 1024)} MB.`); + } + const bytes = new Uint8Array(await file.arrayBuffer()); + // Check the magic number, not the client-supplied MIME type. + if (bytes.length < 5 || new TextDecoder().decode(bytes.slice(0, 5)) !== "%PDF-") { + return json(415, "Only PDF files are supported."); + } + + let resume: { extraction: RawExtraction; userInputs: UserInput[] } | undefined; + const extraction = form.get("extraction"); + const userInputs = form.get("userInputs"); + if (typeof extraction === "string" && typeof userInputs === "string") { + try { + const inputs = JSON.parse(userInputs) as unknown; + if (!Array.isArray(inputs) || !inputs.every((i) => typeof i?.path === "string" && typeof i?.value === "string")) { + return json(400, "userInputs must be a list of {path, value}."); + } + resume = { extraction: JSON.parse(extraction) as RawExtraction, userInputs: inputs as UserInput[] }; + } catch { + return json(400, "Could not read the resume data."); + } + } + + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + async start(controller) { + const send = (event: RunEvent) => controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`)); + try { + await streamRun(bytes, send, resume); + } catch (e) { + // Never echo internal errors (they may contain configuration details). + const message = e instanceof UserFacingError ? e.message : "The run failed unexpectedly. Please try again."; + if (!(e instanceof UserFacingError)) console.error("run failed:", e instanceof Error ? e.name : "unknown"); + send({ type: "error", message }); + } finally { + controller.close(); + } + }, + }); + + return new Response(stream, { + headers: { + "Content-Type": "text/event-stream; charset=utf-8", + "Cache-Control": "no-cache, no-transform", + "X-Accel-Buffering": "no", + }, + }); +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css new file mode 100644 index 0000000..8ff47d0 --- /dev/null +++ b/apps/web/app/globals.css @@ -0,0 +1,116 @@ +@import "tailwindcss"; +@import "@fontsource/barlow/400.css"; +@import "@fontsource/barlow/500.css"; +@import "@fontsource/barlow/600.css"; +@import "@fontsource/barlow/700.css"; +@import "@fontsource/barlow-condensed/600.css"; +@import "@fontsource/barlow-condensed/700.css"; +@import "@fontsource/jetbrains-mono/400.css"; +@import "@fontsource/jetbrains-mono/500.css"; + +/* Verdict: a German office routing slip. Flat colour, square corners, hairline rules. */ +@theme { + --color-paper: #f2f3ee; + --color-sheet: #fbfbf8; + --color-rule: #d5d7cf; + --color-rule-strong: #b6b9ae; + --color-ink: #1c1f22; + --color-ink-2: #4a4f54; + --color-ink-3: #62676d; + --color-office: #2f5d4a; + --color-office-ink: #e8efe9; + --color-office-2: #3c6f59; + --color-stamp: #4536a8; + --color-stamp-wash: #e7e4f6; + --color-void: #c2382b; + --color-void-wash: #f6e3df; + --color-tab: #e7c43f; + --color-tab-wash: #f7eec4; + --color-tab-ink: #5a4708; + --color-quote: #f3e7a0; + + --font-sans: "Barlow", ui-sans-serif, system-ui, sans-serif; + --font-stamp: "Barlow Condensed", "Barlow", ui-sans-serif, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace; + + --text-2xs: 0.75rem; + --text-2xs--line-height: 1rem; +} + +@layer base { + html { + background: var(--color-paper); + color: var(--color-ink); + font-family: var(--font-sans); + font-feature-settings: "tnum" 1, "lnum" 1; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + ::selection { + background: var(--color-stamp); + color: #fff; + } + :focus-visible { + outline: 2px solid var(--color-stamp); + outline-offset: 2px; + } + * { + scrollbar-width: thin; + scrollbar-color: var(--color-rule-strong) transparent; + } + a { + text-underline-offset: 3px; + text-decoration-thickness: 1px; + } + input, + textarea { + caret-color: var(--color-stamp); + } + input::placeholder { + color: var(--color-ink-3); + } +} + +/* The inspection stamp: double rule, condensed caps, violet ink pressed onto the sheet. */ +@keyframes stamp-press { + 0% { + opacity: 0; + transform: rotate(var(--stamp-rot, -4deg)) scale(1.35); + filter: blur(2px); + } + 60% { + opacity: 1; + transform: rotate(var(--stamp-rot, -4deg)) scale(0.97); + filter: blur(0); + } + 100% { + opacity: 1; + transform: rotate(var(--stamp-rot, -4deg)) scale(1); + } +} +.stamp-press { + animation: stamp-press 420ms cubic-bezier(0.16, 1, 0.3, 1) both; +} +@keyframes slip-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: none; + } +} +.slip-in { + animation: slip-in 220ms cubic-bezier(0.16, 1, 0.3, 1) both; +} +@media (prefers-reduced-motion: reduce) { + .stamp-press, + .slip-in, + .animate-pulse { + animation: none; + } + html { + scroll-behavior: auto; + } +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx new file mode 100644 index 0000000..5d43f81 --- /dev/null +++ b/apps/web/app/layout.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react"; +import "./globals.css"; + +export const metadata = { + title: "Verdict", + description: "PDF invoice to valid XRechnung. Nemotron proposes, the official KoSIT validator decides.", +}; + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx new file mode 100644 index 0000000..808bdc2 --- /dev/null +++ b/apps/web/app/page.tsx @@ -0,0 +1,5 @@ +import { VerdictApp } from "@/components/verdict-app.tsx"; + +export default function Home() { + return ; +} diff --git a/apps/web/components/facts-panel.tsx b/apps/web/components/facts-panel.tsx new file mode 100644 index 0000000..a968cdf --- /dev/null +++ b/apps/web/components/facts-panel.tsx @@ -0,0 +1,262 @@ +"use client"; +import type { Provenance } from "@verdict/core"; +import type { RunResultView } from "@/lib/run-protocol.ts"; +import { FIELD_GROUPS, labelFor, valueAt } from "@/lib/field-groups.ts"; +import { VoidMark } from "./verdict-stamp.tsx"; + +export interface FocusTarget { + readonly path: string; + readonly page: number; + readonly quote: string; +} + +export const euro = (v: string | undefined) => { + if (v === undefined) return "–"; + const [int, frac = "00"] = v.split("."); + const grouped = (int ?? "0").replace(/\B(?=(\d{3})+(?!\d))/g, "."); + return `${grouped},${frac.padEnd(2, "0").slice(0, 2)} €`; +}; + +function ProvenanceChip({ p }: { p: Provenance | undefined }) { + if (!p) return —; + if (p.kind === "evidence") return p.{p.page}; + if (p.kind === "user") return you; + return calc; +} + +export function FactsPanel({ + result, + partial, + provenance, + onFocus, +}: { + result: RunResultView; + partial: unknown; + provenance: ReadonlyMap; + onFocus: (target: FocusTarget | null) => void; +}) { + const lines = ((partial as { lines?: Record[] })?.lines ?? []) as Record[]; + const missing = new Set(result.missing.map((m) => m.path)); + const focus = (path: string) => { + const p = provenance.get(path); + onFocus(p?.kind === "evidence" ? { path, page: p.page, quote: p.quote } : null); + }; + + return ( +
+ {FIELD_GROUPS.map((g) => ( +
+

+ {g.title} +

+
+ {g.fields.map((f) => { + const value = valueAt(partial, f.path); + const isMissing = missing.has(f.path) || (f.path.endsWith("electronicAddress.value") && missing.has(f.path.replace(/\.value$/, ""))); + if (value === undefined && !isMissing) return null; + const p = provenance.get(f.path); + return ( +
focus(f.path)} + onMouseLeave={() => onFocus(null)} + onFocus={() => focus(f.path)} + onBlur={() => onFocus(null)} + className={[ + "group grid grid-cols-[3.5rem_1fr_2rem] items-baseline gap-x-3 border-b border-rule py-1.5 text-sm outline-none sm:grid-cols-[4.25rem_minmax(0,9rem)_1fr_2.25rem]", + p?.kind === "evidence" ? "cursor-default hover:bg-stamp-wash focus-visible:bg-stamp-wash" : "", + isMissing ? "bg-tab-wash" : "", + ].join(" ")} + > +
+ {f.bt} + {f.label} +
+
+ {isMissing ? "Not on the document" : {value}} +
+
{isMissing ? null : }
+
+ ); + })} +
+
+ ))} + + {lines.length > 0 && ( +
+

+ Lines +

+
+ + + + + + + + + + + + + {lines.map((l, i) => { + const p = provenance.get(`lines.${i}.name`); + return ( + focus(`lines.${i}.name`)} + onMouseLeave={() => onFocus(null)} + onFocus={() => focus(`lines.${i}.name`)} + onBlur={() => onFocus(null)} + className="border-b border-rule align-baseline outline-none hover:bg-stamp-wash focus-visible:bg-stamp-wash" + title={p?.kind === "evidence" ? `“${p.quote}”` : undefined} + > + + + + + + + + ); + })} + +
BT-126BT-153 ItemBT-129BT-130BT-146VAT
{l.id ?? "–"}{l.name ?? missing}{l.quantity ?? "–"}{l.unitCode ?? "–"}{l.netPrice ? euro(l.netPrice) : "–"} + {l.vatCategory ?? "–"} + {l.vatRate ? ` ${l.vatRate} %` : ""} +
+
+
+ )} + + {result.amounts && ( +
+

+ Totals +

+
+
Totals
+
+
BT-106 net
+
{euro(result.amounts.lineTotal)}
+
BT-110 VAT
+
{euro(result.amounts.taxTotal)}
+
BT-112 total
+
{euro(result.amounts.grandTotal)}
+
+
+
+ )} + +
+ ); +} + +const canonWord = (w: string) => w.normalize("NFKC").toLowerCase().replace(/[.,;:()"„“]/g, ""); + +/** Plain-language reasons for the guards' machine codes. */ +function reasonText(code: string): string { + if (code === "quote-not-on-page") return "Its quote is not on the page word for word"; + if (code === "value-not-in-quote") return "The value is not inside its own quote"; + if (code === "page-out-of-range") return "Cites a page that does not exist"; + if (code === "empty-quote") return "No quote from the page"; + if (code.startsWith("same evidence already used for ")) return `Already used as ${code.slice("same evidence already used for ".length)}`; + if (code.startsWith("evidence: ")) return reasonText(code.slice(10)); + if (code === "city without letters") return "A city needs letters; this looks like a postcode"; + return code.charAt(0).toUpperCase() + code.slice(1); +} + +function fieldName(path: string): { label: string; bt: string } { + const line = /^lines\.(\d+)\.(\w+)/.exec(path); + if (line) return { label: `Line ${Number(line[1]) + 1}: ${line[2]}`, bt: "" }; + const known = labelFor(path); + return known.bt ? known : { label: path, bt: "" }; +} + +/** + * Refused facts and voided patches stay on record. Only the words that are not on the page are + * struck and marked, so a one-word misreading does not look like a refused paragraph. + */ +/** + * Word-by-word comparison with the page: finds where the value's word sequence best lines up + * with the page and returns the first value word that differs, plus the page's word there. + */ +function divergence(words: readonly string[], pageWords: readonly string[]): { at: number; page?: string } | null { + const v = words.map(canonWord); + let best: { at: number; page?: string } | null = null; + let bestLen = -1; + pageWords.forEach((pw, k) => { + if (pw !== v[0]) return; + let n = 0; + while (n < v.length && pageWords[k + n] === v[n]) n++; + if (n > bestLen) { + bestLen = n; + const pageWord = pageWords[k + n]; + best = n >= v.length ? null : { at: n, ...(pageWord !== undefined ? { page: pageWord } : {}) }; + } + }); + return bestLen < 0 ? { at: 0 } : best; +} + +export function VoidedList({ result, pages }: { result: RunResultView; pages: readonly string[] }) { + const pageWords = pages.join(" ").split(/\s+/).map(canonWord).filter(Boolean); + const rows = [ + ...result.rejectedFacts.map((r) => ({ path: r.path, value: r.value, why: r.reason })), + ...result.patchesRejected.map((r) => ({ + path: r.patch.path.replace(/^\//, "").replaceAll("/", "."), + value: r.patch.value, + why: r.reason, + })), + ]; + if (rows.length === 0) return null; + return ( +
+

+ Refused, not hidden +

+
    + {rows.map((r, i) => { + const { label, bt } = fieldName(r.path); + const words = (r.value || "").split(/\s+/).filter(Boolean); + const notOnPage = r.why === "quote-not-on-page" || r.why === "evidence: quote-not-on-page"; + const diff = notOnPage || r.why === "value-not-in-quote" ? divergence(words, pageWords) : null; + const at = diff ? diff.at : -1; + return ( +
  • +
    +
    + {label} + {bt && {bt}} + · {reasonText(r.why)} +
    +

    + {at >= 0 ? ( + <> + {at > 4 && "… "} + {words.slice(Math.max(0, at - 4), at).join(" ")}{" "} + + {words[at]} + {" "} + {words.slice(at + 1, at + 4).join(" ")} + {at + 4 < words.length && " …"} + + {diff?.page ? `the page prints “${diff.page}”` : "not on the page"} + + + ) : ( + {r.value || "(empty)"} + )} +

    +
    + +
  • + ); + })} +
+
+ ); +} diff --git a/apps/web/components/intake.tsx b/apps/web/components/intake.tsx new file mode 100644 index 0000000..7cd0475 --- /dev/null +++ b/apps/web/components/intake.tsx @@ -0,0 +1,134 @@ +"use client"; +import { useEffect, useRef, useState } from "react"; +import { FileUp } from "lucide-react"; + +export interface GalleryCaseSummary { + readonly id: string; + readonly title: string; + readonly story: string; + readonly state: string; + readonly followUpState?: string; +} + +const MAX_MB = 5; + +/** In-tray: drop a PDF, or take one of the recorded runs from the tray. */ +export function Intake({ + onFile, + onGallery, + live, +}: { + onFile: (file: File) => void; + onGallery: (id: string) => void; + live: boolean; +}) { + const [cases, setCases] = useState([]); + const [drag, setDrag] = useState(false); + const [problem, setProblem] = useState(null); + const input = useRef(null); + + useEffect(() => { + fetch("/gallery/index.json") + .then((r) => r.json()) + .then((d: { cases: GalleryCaseSummary[] }) => setCases(d.cases)) + .catch(() => setCases([])); + }, []); + + const take = (file: File | undefined) => { + if (!file) return; + if (file.type && file.type !== "application/pdf") return setProblem("Only PDF files are supported."); + if (file.size > MAX_MB * 1024 * 1024) return setProblem(`The file is larger than ${MAX_MB} MB.`); + setProblem(null); + onFile(file); + }; + + return ( +
+
{ + e.preventDefault(); + setDrag(true); + }} + onDragLeave={() => setDrag(false)} + onDrop={(e) => { + e.preventDefault(); + setDrag(false); + take(e.dataTransfer.files[0]); + }} + className={[ + "flex min-h-64 flex-col items-center justify-center gap-3 border-2 border-dashed px-6 py-10 text-center transition-colors", + drag ? "border-stamp bg-stamp-wash" : "border-rule-strong bg-sheet", + live ? "" : "opacity-60", + ].join(" ")} + > + +

Drop a PDF invoice here

+

+ Text-based PDF, up to {MAX_MB} MB and 5 pages. The file is processed in memory and not stored. +

+ + take(e.target.files?.[0])} + /> + {!live &&

Today’s demo budget is used up. The recorded runs below still work.

} + {problem && ( +

+ {problem} +

+ )} +
+ +
+

+ Recorded runs +

+

Real runs on synthetic invoices, replayed with their original timing.

+
    + {cases.map((c) => ( +
  • + +
  • + ))} +
+
+
+ ); +} + +function OutcomeTag({ state }: { state: string }) { + const verified = state.endsWith("OUTPUT"); + const asks = state.startsWith("NEEDS_INPUT"); + return ( + + {asks && verified ? "asks · verified" : asks ? "asks" : verified ? "verified" : "rejected"} + + ); +} diff --git a/apps/web/components/needs-input.tsx b/apps/web/components/needs-input.tsx new file mode 100644 index 0000000..51095a3 --- /dev/null +++ b/apps/web/components/needs-input.tsx @@ -0,0 +1,77 @@ +"use client"; +import { useState } from "react"; +import type { UserInput } from "@verdict/core"; +import type { RunResultView } from "@/lib/run-protocol.ts"; +import { labelFor } from "@/lib/field-groups.ts"; + +/** UI-04: ask only for the facts that are really missing, then re-validate with provenance "user". */ +export function NeedsInput({ + result, + prefill, + onSubmit, +}: { + result: RunResultView; + prefill: readonly UserInput[] | null; + onSubmit: (inputs: UserInput[]) => void; +}) { + const asks = result.missing.map((m) => ({ + path: m.path.endsWith("electronicAddress") ? `${m.path}.value` : m.path, + reason: m.reason, + })); + const [values, setValues] = useState>(() => + Object.fromEntries(asks.map((a) => [a.path, prefill?.find((p) => p.path === a.path)?.value ?? ""])), + ); + + if (asks.length === 0) { + return ( +
+

The document contradicts itself here, so Verdict stopped instead of guessing.

+
    + {result.openIssues.map((i) => ( +
  • {i.message}
  • + ))} +
+
+ ); + } + + return ( +
{ + e.preventDefault(); + onSubmit(asks.map((a) => ({ path: a.path, value: (values[a.path] ?? "").trim() }))); + }} + > +

+ {asks.length === 1 ? "One fact is" : `${asks.length} facts are`} required for an XRechnung but not printed on the document. + Verdict will not invent {asks.length === 1 ? "it" : "them"}. +

+
+ {asks.map((a) => { + const { label, bt } = labelFor(a.path); + return ( + + ); + })} +
+ +
+ ); +} diff --git a/apps/web/components/pdf-sheet.tsx b/apps/web/components/pdf-sheet.tsx new file mode 100644 index 0000000..c435f2c --- /dev/null +++ b/apps/web/components/pdf-sheet.tsx @@ -0,0 +1,163 @@ +"use client"; +import { useEffect, useMemo, useRef, useState } from "react"; + +export interface QuoteMark { + readonly page: number; + readonly quote: string; + readonly kind: "evidence" | "active" | "void"; +} + +interface TextBox { + readonly str: string; + readonly x: number; + readonly y: number; + readonly w: number; + readonly h: number; +} + +interface RenderedPage { + readonly width: number; + readonly height: number; + readonly image: string; + readonly boxes: readonly TextBox[]; +} + +const canon = (s: string) => + s.normalize("NFKC").replace(/[‐-―−]/g, "-").replace(/\s+/g, "").toLowerCase(); + +/** Boxes of the text items that together contain the quote (whitespace-insensitive). */ +function boxesForQuote(boxes: readonly TextBox[], quote: string): TextBox[] { + const target = canon(quote); + if (!target) return []; + let text = ""; + const owner: number[] = []; + boxes.forEach((b, i) => { + const c = canon(b.str); + text += c; + for (let k = 0; k < c.length; k++) owner.push(i); + }); + const at = text.indexOf(target); + if (at < 0) return []; + const hit = new Set(owner.slice(at, at + target.length)); + return [...hit].map((i) => boxes[i]!).filter(Boolean); +} + +/** + * The invoice as printed, rendered with pdf.js. The page never moves between states (it stays + * registered); only the marks on it change: faint yellow for every quoted fact, violet for the + * fact in focus, red for refused quotes. + */ +export function PdfSheet({ url, marks, focusPage }: { url: string; marks: readonly QuoteMark[]; focusPage?: number }) { + const [pages, setPages] = useState([]); + const [failed, setFailed] = useState(false); + const pageRefs = useRef<(HTMLDivElement | null)[]>([]); + + useEffect(() => { + let cancelled = false; + setPages([]); + setFailed(false); + (async () => { + try { + const pdfjs = await import("pdfjs-dist"); + pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"; + const doc = await pdfjs.getDocument({ url }).promise; + const out: RenderedPage[] = []; + for (let n = 1; n <= doc.numPages; n++) { + const page = await doc.getPage(n); + const viewport = page.getViewport({ scale: 2 }); + const canvas = document.createElement("canvas"); + canvas.width = viewport.width; + canvas.height = viewport.height; + await page.render({ canvas, viewport }).promise; + const content = await page.getTextContent(); + const boxes: TextBox[] = []; + for (const item of content.items) { + if (!("str" in item) || !item.str.trim()) continue; + const h = item.height || Math.abs(item.transform[3]); + const [x0, y0] = viewport.convertToViewportPoint(item.transform[4], item.transform[5]) as [number, number]; + const [x1, y1] = viewport.convertToViewportPoint(item.transform[4] + item.width, item.transform[5] + h) as [ + number, + number, + ]; + boxes.push({ + str: item.str, + x: Math.min(x0, x1) / viewport.width, + y: Math.min(y0, y1) / viewport.height, + w: Math.abs(x1 - x0) / viewport.width, + h: Math.abs(y1 - y0) / viewport.height, + }); + } + out.push({ width: viewport.width, height: viewport.height, image: canvas.toDataURL("image/png"), boxes }); + } + if (!cancelled) setPages(out); + } catch { + if (!cancelled) setFailed(true); + } + })(); + return () => { + cancelled = true; + }; + }, [url]); + + const overlays = useMemo( + () => + pages.map((p, i) => + marks + .filter((m) => m.page === i + 1) + .flatMap((m) => boxesForQuote(p.boxes, m.quote).map((b) => ({ ...b, kind: m.kind }))), + ), + [pages, marks], + ); + + useEffect(() => { + if (focusPage) pageRefs.current[focusPage - 1]?.scrollIntoView({ + behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth", + block: "nearest", + }); + }, [focusPage]); + + if (failed) { + return

The PDF preview could not be rendered in this browser.

; + } + if (pages.length === 0) { + return ( +
+ ); + } + return ( +
+ {pages.map((p, i) => ( +
{ + pageRefs.current[i] = el; + }} + className="relative bg-white shadow-[0_1px_2px_rgba(28,31,34,0.08),0_8px_24px_-12px_rgba(28,31,34,0.18)]" + style={{ aspectRatio: `${p.width} / ${p.height}` }} + > + {/* eslint-disable-next-line @next/next/no-img-element */} + {`Invoice + {overlays[i]?.map((b, k) => ( + + ))} +
+ ))} +
+ ); +} diff --git a/apps/web/components/routing-slip.tsx b/apps/web/components/routing-slip.tsx new file mode 100644 index 0000000..13bdd4c --- /dev/null +++ b/apps/web/components/routing-slip.tsx @@ -0,0 +1,139 @@ +"use client"; +import type { AuditEvent } from "@verdict/core"; +import { IDLE_ROUTE, STATIONS, stationNote } from "@/lib/stations.ts"; + +const fmtDuration = (ms: number) => (ms < 1000 ? `${ms} ms` : `${(ms / 1000).toFixed(2)} s`); +const clock = (iso: string) => new Date(iso).toLocaleTimeString("de-DE", { hour12: false }); + +type Tone = "stamp" | "void" | "tab" | "office" | "ink"; + +function toneOf(e: AuditEvent): Tone { + const d = e.data as { valid?: boolean; lineMismatches?: number }; + if (e.type === "VALIDATE") return d.valid ? "stamp" : "void"; + if (e.type === "NEEDS_INPUT") return "tab"; + if (e.type === "USER_INPUT") return "tab"; + if (e.type === "CONSISTENCY_CHECK" && Number(d.lineMismatches ?? 0) > 0) return "void"; + return STATIONS[e.type].actor === "model" ? "office" : "ink"; +} + +const SIGN: Record = { + stamp: "border-stamp text-stamp", + void: "border-void text-void", + tab: "border-tab-ink bg-tab text-tab-ink", + office: "border-office text-office", + ink: "border-ink-2 text-ink-2", +}; +const BAR: Record = { + stamp: "bg-stamp", + void: "bg-void", + tab: "bg-tab", + office: "bg-office", + ink: "bg-ink-2", +}; + +/** + * The run as a German routing slip (Laufzettel): a ruled form with one row per station, the + * station's own sign-off initials, and how long it really took. A resumed run (after the user + * answered a question) continues on the same slip below a divider; nothing is erased. + */ +export function RoutingSlip({ + events, + running, + fileRef, +}: { + events: readonly AuditEvent[]; + running: boolean; + /** Upload hash, shown as the file reference at the head of the slip. */ + fileRef?: string | undefined; +}) { + // An audit event is written when its station finishes, so a station took from the previous + // stamp to its own. A new pass (seq restarts at 0) starts its own clock. + const durations = events.map((e, i) => { + const prev = events[i - 1]; + return prev && e.seq > 0 ? new Date(e.ts).getTime() - new Date(prev.ts).getTime() : 0; + }); + const longest = Math.max(1, ...durations); + const total = durations.reduce((a, b) => a + b, 0); + + return ( +
+
+
+

Routing slip

+ + {events.length ? `${events.length} stamps · ${fmtDuration(total)}` : "Awaiting an invoice"} + +
+ {fileRef && ( +
+ File ref. + {fileRef.replace(/^sha256:/, "").slice(0, 16)} +
+ )} +
+ +
+ Station + Signed + Took +
+ +
    + {events.length === 0 + ? IDLE_ROUTE.map((type) => ( +
  1. + {STATIONS[type].label} + + {STATIONS[type].initials} + + +
  2. + )) + : events.map((e, i) => { + const s = STATIONS[e.type]; + const tone = toneOf(e); + const note = stationNote(e); + return ( +
  3. + {e.seq === 0 && i > 0 && ( +
    + Returned · answered by you · second pass +
    + )} +
    +
    +
    + {s.label} +
    + {note &&

    {note}

    } + {durations[i]! > 0 && ( +
    + )} +
    + + {s.initials} + + + {durations[i]! > 0 ? fmtDuration(durations[i]!) : "–"} + +
    +
  4. + ); + })} + {running && ( +
  5. + At the next desk + + +
  6. + )} +
+
+ ); +} diff --git a/apps/web/components/verdict-app.tsx b/apps/web/components/verdict-app.tsx new file mode 100644 index 0000000..0a302eb --- /dev/null +++ b/apps/web/components/verdict-app.tsx @@ -0,0 +1,310 @@ +"use client"; +import { useEffect, useMemo, useState } from "react"; +import type { Provenance } from "@verdict/core"; +import { RULE_FIELDS } from "@verdict/core/rule-fields"; +import { labelFor } from "@/lib/field-groups.ts"; +import { ArrowLeft, Download } from "lucide-react"; +import { useRun } from "@/lib/use-run.ts"; +import { euro, FactsPanel, VoidedList, type FocusTarget } from "./facts-panel.tsx"; +import { Intake } from "./intake.tsx"; +import { NeedsInput } from "./needs-input.tsx"; +import { PdfSheet, type QuoteMark } from "./pdf-sheet.tsx"; +import { RoutingSlip } from "./routing-slip.tsx"; +import { VerdictStamp, VoidMark } from "./verdict-stamp.tsx"; + +interface Budget { + readonly live: boolean; + readonly spentUsd: string; + readonly budgetUsd: string; +} + +const download = (name: string, content: string, type: string) => { + const url = URL.createObjectURL(new Blob([content], { type })); + const a = Object.assign(document.createElement("a"), { href: url, download: name }); + a.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); +}; + +export function VerdictApp() { + const { state, runUpload, runGallery, answer, reset, galleryAnswer } = useRun(); + const [budget, setBudget] = useState(null); + const [focus, setFocus] = useState(null); + const [tab, setTab] = useState<"document" | "facts" | "slip">("facts"); + + useEffect(() => { + fetch("/api/budget") + .then((r) => r.json()) + .then(setBudget) + .catch(() => setBudget({ live: false, spentUsd: "0", budgetUsd: "0" })); + }, [state.phase]); + + const result = state.result; + const provenance = useMemo(() => new Map(result?.provenance ?? []), [result]); + const marks = useMemo(() => { + const out: QuoteMark[] = []; + for (const [, p] of provenance) if (p.kind === "evidence") out.push({ page: p.page, quote: p.quote, kind: "evidence" }); + if (focus) out.push({ page: focus.page, quote: focus.quote, kind: "active" }); + return out; + }, [provenance, focus]); + + const idle = state.phase === "idle"; + const verifier = result?.verifier; + const stampKind = result ? (result.state === "OUTPUT" ? "verified" : result.state === "NEEDS_INPUT" ? "returned" : "rejected") : null; + // The stamp carries the run's own facts: date of the VALIDATE stamp, pinned configuration. + const validateEvent = [...state.events].reverse().find((e) => e.type === "VALIDATE"); + const stampDate = validateEvent?.ts.slice(0, 10) ?? ""; + const xrVersion = /xrechnung-(\d+\.\d+\.\d+)/i.exec(verifier?.versions.configuration ?? "")?.[1]; + const reportHash = verifier?.reportHash?.slice(0, 10); + const printedTotal = result?.documentTotals.grandTotal; + const totalDiffers = Boolean(result?.amounts && printedTotal && printedTotal !== result.amounts.grandTotal); + const runCost = state.priorCostUsd + Number(result?.costUsd ?? 0); + const runIterations = state.priorIterations + (result?.iterations ?? 0); + + return ( +
+
+
+ + + PDF invoice to XRechnung. Nemotron proposes, the official validator decides. + +
+ + NVIDIA Nemotron on Nebius Token Factory · KoSIT validator {verifier?.versions.validator ?? "1.6.3"} + + {budget && ( + + {budget.live ? `$${budget.spentUsd} / $${budget.budgetUsd} today` : "live runs paused"} + + )} +
+
+
+
+ NVIDIA Nemotron on Nebius Token Factory proposes. KoSIT decides. +
+ + {/* Mobile: one work area at a time. Desktop: all three side by side. */} + {!idle && ( + + )} + +
+
+ {idle ? ( + <> + void runGallery(id)} live={budget?.live ?? false} /> +
+ +
+ + ) : ( +
+
+ + + {state.source?.kind === "gallery" ? `recorded run · ${state.source.title}` : state.source?.name} + +
+ {state.pdfUrl && } +
+ )} +
+ +
+ {idle ? ( + + ) : ( +
+
+ {stampKind && result ? ( + + ) : state.phase === "error" ? ( +

+ {state.error} +

+ ) : ( +

On its way through the office…

+ )} + {result?.amounts && result.state === "OUTPUT" && ( +
+
+ BT-112 Total, computed from the lines +
+
+ {euro(result.amounts.grandTotal)} +
+ {totalDiffers && ( +
+ Printed: + {euro(printedTotal)} + +
+ )} +
+ )} + {result && ( +
+

+ {result.state === "OUTPUT" + ? "Every field is quoted from the page or computed. The official KoSIT validator accepted the XRechnung." + : result.state === "NEEDS_INPUT" + ? "Some required facts are not on the document. Verdict asks instead of inventing them." + : "The validator rejected the result."} +

+

+ {runIterations} repair round{runIterations === 1 ? "" : "s"} · model cost{" "} + ${runCost.toFixed(4)} +

+
+ {result.xml && ( + + )} + +
+
+ )} +
+ + {result?.state === "NEEDS_INPUT" && ( + + )} + {result && } + {result?.verifier && !result.verifier.valid && ( +
    + {result.verifier.errors + .filter((e) => e.severity === "error") + .map((e) => ( +
  • +
    + {e.ruleId} + {RULE_FIELDS[e.ruleId] + ? `${labelFor(RULE_FIELDS[e.ruleId]!).label} is required but not on the document. Asked above.` + : "Rule violation reported by the KoSIT validator."} +
    + {/* The validator's own wording stays as evidence, without its duplicated rule prefix. */} +

    {e.message.replace(/^\[[^\]]+\]\s*-?\s*/, "")}

    +
  • + ))} +
+ )} + {result ? ( + + ) : ( + + )} +
+ )} +
+ + +
+
+ ); +} + +function Explainer() { + return ( +
+

+ An e-invoice is only valid when the official validator says so. +

+

+ Drop a PDF: Nemotron quotes every field from the page, the KoSIT validator decides, and missing facts are asked for. +

+

+ Verdict turns a PDF invoice into an XRechnung. NVIDIA Nemotron reads the page and proposes every field with a + verbatim quote. Amounts are computed, never written by the model. The KoSIT reference validator has the last word, + and facts that are not on the document are asked for, never invented. +

+
+ +
+
+ ); +} + +function Principles() { + return ( +
+
Quoted
+
Every value is pinned to its place on the page. Hover a fact to see it.
+
Computed
+
Totals and VAT come from the lines; a printed total that disagrees is flagged, not copied.
+
Refused
+
Values that fail a check stay on record, struck through, instead of quietly disappearing.
+
Signed
+
Every station signs the routing slip into a hash chain you can verify yourself.
+
+ ); +} + +function FactsSkeleton() { + return ( +
+ {[6, 8, 4].map((n, g) => ( +
+
+ {Array.from({ length: n }).map((_, i) => ( +
+
+
+
+
+ ))} +
+ ))} +
+ ); +} diff --git a/apps/web/components/verdict-stamp.tsx b/apps/web/components/verdict-stamp.tsx new file mode 100644 index 0000000..686dca8 --- /dev/null +++ b/apps/web/components/verdict-stamp.tsx @@ -0,0 +1,53 @@ +"use client"; + +export type StampKind = "verified" | "rejected" | "returned"; + +const STYLES: Record = { + verified: { color: "text-stamp border-stamp", title: "Verified", rot: "-4deg" }, + rejected: { color: "text-void border-void", title: "Rejected", rot: "3deg" }, + returned: { color: "text-tab-ink border-tab-ink", title: "Returned", rot: "-3deg" }, +}; + +/** + * The signature move: the verdict is pressed onto the sheet like an office inspection stamp, + * double-ruled, condensed caps, with the validator version, date and report hash. + */ +export function VerdictStamp({ + kind, + lines, + hash, +}: { + kind: StampKind; + /** Small caps lines under the title, e.g. validator version and date. */ + lines: readonly string[]; + /** Report hash, set exactly as on the routing slip (lowercase mono). */ + hash?: string | undefined; +}) { + const s = STYLES[kind]; + return ( +
+
+
{s.title}
+ {lines.map((l) => ( +
+ {l} +
+ ))} + {hash &&
report {hash}
} +
+
+ ); +} + +/** A small VOID stamp for refused values and patches. Nothing disappears; it is cancelled. */ +export function VoidMark({ label = "Void" }: { label?: string }) { + return ( + + {label} + + ); +} diff --git a/apps/web/lib/engine.ts b/apps/web/lib/engine.ts new file mode 100644 index 0000000..e004be9 --- /dev/null +++ b/apps/web/lib/engine.ts @@ -0,0 +1,16 @@ +import "server-only"; +import { TokenFactoryClient } from "@verdict/core"; + +/** One client per server process, so the daily budget ledger is shared by all requests. */ +let client: TokenFactoryClient | undefined; +export function engineClient(): TokenFactoryClient { + client ??= TokenFactoryClient.fromEnv(); + return client; +} + +export const MODELS = { + extract: process.env.MODEL_EXTRACT || "nvidia/nemotron-3-super-120b-a12b", + repair: process.env.MODEL_REPAIR || "nvidia/Nemotron-3-Ultra-550b-a55b", +}; + +export const DAILY_BUDGET_USD = process.env.DAILY_BUDGET_USD || "2.00"; diff --git a/apps/web/lib/field-groups.ts b/apps/web/lib/field-groups.ts new file mode 100644 index 0000000..a9fcd16 --- /dev/null +++ b/apps/web/lib/field-groups.ts @@ -0,0 +1,80 @@ +/** Which facts the result view shows, grouped as on a German invoice, with their business terms. */ +export interface FieldDef { + readonly path: string; + readonly label: string; + readonly bt: string; +} + +export interface FieldGroup { + readonly title: string; + readonly fields: readonly FieldDef[]; +} + +export const FIELD_GROUPS: readonly FieldGroup[] = [ + { + title: "Invoice", + fields: [ + { path: "number", label: "Invoice number", bt: "BT-1" }, + { path: "issueDate", label: "Issue date", bt: "BT-2" }, + { path: "dueDate", label: "Due date", bt: "BT-9" }, + { path: "buyerReference", label: "Leitweg-ID / buyer reference", bt: "BT-10" }, + { path: "orderReference", label: "Order reference", bt: "BT-13" }, + { path: "currency", label: "Currency", bt: "BT-5" }, + ], + }, + { + title: "Seller", + fields: [ + { path: "seller.name", label: "Name", bt: "BT-27" }, + { path: "seller.vatId", label: "VAT ID", bt: "BT-31" }, + { path: "seller.taxNumber", label: "Tax number", bt: "BT-32" }, + { path: "seller.legalRegistrationId", label: "Register entry", bt: "BT-30" }, + { path: "seller.address.line1", label: "Street", bt: "BT-35" }, + { path: "seller.address.postcode", label: "Postcode", bt: "BT-38" }, + { path: "seller.address.city", label: "City", bt: "BT-37" }, + { path: "seller.address.countryCode", label: "Country", bt: "BT-40" }, + { path: "seller.electronicAddress.value", label: "Electronic address", bt: "BT-34" }, + { path: "seller.contact.name", label: "Contact", bt: "BT-41" }, + { path: "seller.contact.phone", label: "Phone", bt: "BT-42" }, + { path: "seller.contact.email", label: "E-mail", bt: "BT-43" }, + ], + }, + { + title: "Buyer", + fields: [ + { path: "buyer.name", label: "Name", bt: "BT-44" }, + { path: "buyer.address.line1", label: "Street", bt: "BT-50" }, + { path: "buyer.address.postcode", label: "Postcode", bt: "BT-53" }, + { path: "buyer.address.city", label: "City", bt: "BT-52" }, + { path: "buyer.address.countryCode", label: "Country", bt: "BT-55" }, + { path: "buyer.electronicAddress.value", label: "Electronic address", bt: "BT-49" }, + ], + }, + { + title: "Payment", + fields: [ + { path: "payment.iban", label: "IBAN", bt: "BT-84" }, + { path: "payment.accountName", label: "Account holder", bt: "BT-85" }, + { path: "paymentTerms", label: "Payment terms", bt: "BT-20" }, + ], + }, +]; + +/** Read a dotted path from the (possibly partial) model. */ +export function valueAt(model: unknown, path: string): string | undefined { + let node: unknown = model; + for (const k of path.split(".")) { + if (node === null || typeof node !== "object") return undefined; + node = (node as Record)[k]; + } + return typeof node === "string" && node !== "" ? node : undefined; +} + +/** Human labels for model paths the user may be asked about. */ +export function labelFor(path: string): { label: string; bt: string } { + for (const g of FIELD_GROUPS) { + const f = g.fields.find((x) => x.path === path); + if (f) return { label: `${g.title}: ${f.label}`, bt: f.bt }; + } + return { label: path, bt: "" }; +} diff --git a/apps/web/lib/limits.ts b/apps/web/lib/limits.ts new file mode 100644 index 0000000..f95b717 --- /dev/null +++ b/apps/web/lib/limits.ts @@ -0,0 +1,23 @@ +/** NFR-04 / NFR-05: upload limits, per-IP rate limit and a global daily model budget. */ +export const MAX_UPLOAD_BYTES = Number(process.env.MAX_UPLOAD_MB || "5") * 1024 * 1024; +export const MAX_PAGES = Number(process.env.MAX_PAGES || "5"); +const RUNS_PER_IP_PER_HOUR = Number(process.env.RUNS_PER_IP_PER_HOUR || "12"); + +const hits = new Map(); + +/** Sliding one-hour window per IP, in memory (single instance, no database by design). */ +export function takeRateLimit(ip: string, now = Date.now()): { ok: boolean; retryAfterS: number } { + const windowStart = now - 3600_000; + const recent = (hits.get(ip) ?? []).filter((t) => t > windowStart); + if (recent.length >= RUNS_PER_IP_PER_HOUR) { + const oldest = recent[0] ?? now; + return { ok: false, retryAfterS: Math.ceil((oldest + 3600_000 - now) / 1000) }; + } + recent.push(now); + hits.set(ip, recent); + return { ok: true, retryAfterS: 0 }; +} + +export function clientIp(headers: Headers): string { + return headers.get("x-forwarded-for")?.split(",")[0]?.trim() || headers.get("x-real-ip") || "local"; +} diff --git a/apps/web/lib/run-core.ts b/apps/web/lib/run-core.ts new file mode 100644 index 0000000..84d54c1 --- /dev/null +++ b/apps/web/lib/run-core.ts @@ -0,0 +1,95 @@ +import { createHash } from "node:crypto"; +import { + BudgetExceededError, + deriveAmounts, + extractPageTexts, + runInvoice, + type RawExtraction, + type TokenFactoryClient, + type UserInput, + type VerifierVerdict, +} from "@verdict/core"; +import type { RunEvent, RunResultView } from "./run-protocol.ts"; +import { MAX_PAGES } from "./limits.ts"; + +export class UserFacingError extends Error {} + +/** + * Runs one invoice through the agent loop and emits RunEvents as they happen. + * No server-only imports here: the gallery builder runs this file outside Next.js. + * Nothing is persisted (NFR-03): the PDF lives only in memory for the duration of the call. + */ +export interface RunDeps { + readonly client: TokenFactoryClient; + readonly models: { readonly extract: string; readonly repair: string }; + readonly validate: (xml: string) => Promise; +} + +/** Shared by the API route and the gallery builder, so both produce identical event streams. */ +export async function executeRun( + deps: RunDeps, + pdf: Uint8Array, + emit: (event: RunEvent) => void, + resume?: { extraction: RawExtraction; userInputs: readonly UserInput[] }, +): Promise { + const uploadHash = `sha256:${createHash("sha256").update(pdf).digest("hex")}`; + let pages: string[]; + try { + pages = await extractPageTexts(Buffer.from(pdf)); + } catch { + throw new UserFacingError("This file could not be read as a PDF."); + } + if (pages.length > MAX_PAGES) throw new UserFacingError(`Please upload at most ${MAX_PAGES} pages.`); + if (pages.every((p) => p.trim().length < 20)) { + throw new UserFacingError( + "This PDF has no text layer (probably a scan). This build reads text-based PDFs only.", + ); + } + emit({ type: "start", pages, uploadHash }); + + try { + const run = await runInvoice({ + client: deps.client, + models: deps.models, + validate: deps.validate, + pages, + uploadHash, + onAudit: (event) => emit({ type: "audit", event }), + ...(resume ? { extraction: resume.extraction, userInputs: resume.userInputs } : {}), + }); + const amounts = run.invoice ? deriveAmounts(run.invoice) : null; + const view: RunResultView = { + state: run.state, + invoice: run.invoice ?? run.partial, + provenance: [...run.provenance.entries()], + missing: run.missing, + openIssues: run.openIssues, + warnings: run.warnings, + rejectedFacts: run.rejectedFacts, + patchesApplied: run.patchesApplied, + patchesRejected: run.patchesRejected, + verifier: run.verifier + ? { + valid: run.verifier.valid, + errors: run.verifier.errors, + reportHash: run.verifier.reportHash, + versions: run.verifier.versions, + } + : null, + amounts: amounts ? { lineTotal: amounts.lineTotal, taxTotal: amounts.taxTotal, grandTotal: amounts.grandTotal } : null, + documentTotals: run.documentTotals, + xml: run.xml ?? null, + auditJsonl: run.audit.toJsonl(), + iterations: run.iterations, + costUsd: run.costUsd, + extraction: run.extraction, + models: deps.models, + }; + emit({ type: "result", result: view }); + } catch (e) { + if (e instanceof BudgetExceededError) { + throw new UserFacingError("Today's model budget for this public demo is used up. The example gallery still works."); + } + throw e; + } +} diff --git a/apps/web/lib/run-protocol.ts b/apps/web/lib/run-protocol.ts new file mode 100644 index 0000000..278d970 --- /dev/null +++ b/apps/web/lib/run-protocol.ts @@ -0,0 +1,37 @@ +import type { AuditEvent, MissingFact, Provenance, RejectedFact, RawExtraction, RunState } from "@verdict/core"; + +/** + * Server-sent events of one run. The UI draws the run sheet from `audit` events (each carries + * its timestamp), and the result view from the final `result`. + */ +export type RunEvent = + | { readonly type: "start"; readonly pages: readonly string[]; readonly uploadHash: string } + | { readonly type: "audit"; readonly event: AuditEvent } + | { readonly type: "result"; readonly result: RunResultView } + | { readonly type: "error"; readonly message: string }; + +export interface RunResultView { + readonly state: RunState; + readonly invoice: unknown; + readonly provenance: readonly (readonly [string, Provenance])[]; + readonly missing: readonly MissingFact[]; + readonly openIssues: readonly { readonly id: string; readonly message: string; readonly location: string }[]; + readonly warnings: readonly string[]; + readonly rejectedFacts: readonly RejectedFact[]; + readonly patchesApplied: readonly { path: string; value: string; normalized: string; quote: string; page: number }[]; + readonly patchesRejected: readonly { patch: { path: string; value: string; quote: string; page: number }; reason: string }[]; + readonly verifier: { + readonly valid: boolean; + readonly errors: readonly { ruleId: string; severity: string; message: string; location: string }[]; + readonly reportHash: string | null; + readonly versions: { validator: string; configuration: string }; + } | null; + readonly amounts: { readonly lineTotal: string; readonly taxTotal: string; readonly grandTotal: string } | null; + readonly documentTotals: { readonly lineTotal?: string | undefined; readonly taxTotal?: string | undefined; readonly grandTotal?: string | undefined }; + readonly xml: string | null; + readonly auditJsonl: string; + readonly iterations: number; + readonly costUsd: string; + readonly extraction: RawExtraction; + readonly models: { readonly extract: string; readonly repair: string }; +} diff --git a/apps/web/lib/run-stream.ts b/apps/web/lib/run-stream.ts new file mode 100644 index 0000000..4b95f44 --- /dev/null +++ b/apps/web/lib/run-stream.ts @@ -0,0 +1,16 @@ +import "server-only"; +import type { RawExtraction, UserInput } from "@verdict/core"; +import { engineClient, MODELS } from "./engine.ts"; +import { executeRun } from "./run-core.ts"; +import type { RunEvent } from "./run-protocol.ts"; +import { validateXRechnung } from "./verifier.ts"; + +export { UserFacingError } from "./run-core.ts"; + +export function streamRun( + pdf: Uint8Array, + emit: (event: RunEvent) => void, + resume?: { extraction: RawExtraction; userInputs: readonly UserInput[] }, +): Promise { + return executeRun({ client: engineClient(), models: MODELS, validate: validateXRechnung }, pdf, emit, resume); +} diff --git a/apps/web/lib/stations.ts b/apps/web/lib/stations.ts new file mode 100644 index 0000000..d7bf9ea --- /dev/null +++ b/apps/web/lib/stations.ts @@ -0,0 +1,84 @@ +import type { AuditEvent } from "@verdict/core"; + +/** Routing-slip stations: each audit event type signs off under its own initials. */ +export interface Station { + readonly label: string; + readonly initials: string; + readonly actor: "model" | "core" | "verifier" | "user"; +} + +export const STATIONS: Readonly> = { + INGEST: { label: "Received", initials: "IN", actor: "core" }, + EXTRACT: { label: "Read by Nemotron Super", initials: "NEM·S", actor: "model" }, + EVIDENCE_CHECK: { label: "Quotes checked", initials: "EVD", actor: "core" }, + NORMALIZE: { label: "Normalized", initials: "NRM", actor: "core" }, + DERIVE: { label: "Amounts computed", initials: "CALC", actor: "core" }, + CONSISTENCY_CHECK: { label: "Totals cross-checked", initials: "CHK", actor: "core" }, + BUILD_CII: { label: "XRechnung written", initials: "CII", actor: "core" }, + VALIDATE: { label: "Official KoSIT validator", initials: "KoSIT", actor: "verifier" }, + REPAIR_PLAN: { label: "Repaired by Nemotron Ultra", initials: "NEM·U", actor: "model" }, + PATCH_GUARD: { label: "Patch guard", initials: "PG", actor: "core" }, + NEEDS_INPUT: { label: "Returned for your input", initials: "RTN", actor: "core" }, + USER_INPUT: { label: "Your answers recorded", initials: "YOU", actor: "user" }, + OUTPUT: { label: "Issued", initials: "OUT", actor: "core" }, +}; + +/** The stations a clean run passes, shown greyed out before anything runs. */ +export const IDLE_ROUTE: readonly AuditEvent["type"][] = [ + "INGEST", + "EXTRACT", + "EVIDENCE_CHECK", + "DERIVE", + "CONSISTENCY_CHECK", + "BUILD_CII", + "VALIDATE", + "OUTPUT", +]; + +/** One-line facts for a station, read from the audit event data (never invoice contents). */ +export function stationNote(e: AuditEvent): string | null { + const d = e.data as Record; + switch (e.type) { + case "INGEST": + return `${d.pages} page${d.pages === 1 ? "" : "s"} · ${short(e.inputHash)}`; + case "EXTRACT": + if (d.reused) return "previous reading reused"; + return `${d.promptTokens} → ${d.completionTokens} tokens · $${fmtCost(d.costUsd)}`; + case "REPAIR_PLAN": + return `${d.promptTokens} → ${d.completionTokens} tokens · $${fmtCost(d.costUsd)}`; + case "EVIDENCE_CHECK": { + const rejected = (d.rejected as unknown[] | undefined)?.length ?? 0; + return `${d.accepted} quoted · ${rejected} refused`; + } + case "PATCH_GUARD": { + const a = (d.accepted as unknown[] | undefined)?.length ?? 0; + const r = (d.rejected as unknown[] | undefined)?.length ?? 0; + return `${a} accepted · ${r} voided`; + } + case "DERIVE": + return `${d.lines} line${d.lines === 1 ? "" : "s"} · ${d.vatGroups} VAT group${d.vatGroups === 1 ? "" : "s"}`; + case "CONSISTENCY_CHECK": { + const lines = Number(d.lineMismatches ?? 0); + const totals = Number(d.totalWarnings ?? 0); + if (lines > 0) return `${lines} line${lines === 1 ? "" : "s"} contradict the page`; + return totals > 0 ? `printed total differs` : "all lines match the page"; + } + case "BUILD_CII": + return short(e.outputHash); + case "VALIDATE": + return d.valid ? `accepted · report ${short(String(d.reportHash ?? ""))}` : `${d.errorCount} rule violation${d.errorCount === 1 ? "" : "s"}`; + case "NEEDS_INPUT": { + const m = (d.missing as unknown[] | undefined)?.length ?? 0; + return m > 0 ? `${m} fact${m === 1 ? "" : "s"} not on the document` : "could not be resolved from the document"; + } + case "USER_INPUT": + return (d.fields as string[] | undefined)?.join(", ") ?? null; + case "OUTPUT": + return "audit chain closed"; + default: + return null; + } +} + +const short = (hash: string | undefined) => (hash ? hash.replace(/^sha256:/, "").slice(0, 10) : ""); +const fmtCost = (v: unknown) => Number(v ?? 0).toFixed(5); diff --git a/apps/web/lib/use-run.ts b/apps/web/lib/use-run.ts new file mode 100644 index 0000000..c5e6fa0 --- /dev/null +++ b/apps/web/lib/use-run.ts @@ -0,0 +1,193 @@ +"use client"; +import { useCallback, useRef, useState } from "react"; +import type { AuditEvent, UserInput } from "@verdict/core"; +import type { RunEvent, RunResultView } from "./run-protocol.ts"; + +export type RunPhase = "idle" | "running" | "done" | "error"; + +export interface RunState { + readonly phase: RunPhase; + readonly source: { kind: "upload"; name: string } | { kind: "gallery"; id: string; title: string } | null; + readonly pdfUrl: string | null; + readonly pages: readonly string[]; + readonly events: readonly AuditEvent[]; + readonly result: RunResultView | null; + readonly error: string | null; + /** Wall-clock start of the run, for live elapsed time. */ + readonly startedAt: number | null; + /** Model cost of earlier passes of this document (a resumed run adds to it). */ + readonly priorCostUsd: number; + readonly priorIterations: number; +} + +const EMPTY: RunState = { + phase: "idle", + source: null, + pdfUrl: null, + pages: [], + events: [], + result: null, + error: null, + startedAt: null, + priorCostUsd: 0, + priorIterations: 0, +}; + +export interface GalleryRecording { + readonly id: string; + readonly title: string; + readonly story: string; + readonly pdf: string; + readonly run: readonly { at: number; event: RunEvent }[]; + readonly answer?: readonly UserInput[]; + readonly followUp?: readonly { at: number; event: RunEvent }[]; +} + +/** Drives one run: live via the API (server-sent events) or replayed from a gallery recording. */ +export function useRun() { + const [state, setState] = useState(EMPTY); + const fileRef = useRef(null); + const recordingRef = useRef(null); + const cancelRef = useRef<() => void>(() => undefined); + + const apply = useCallback((event: RunEvent) => { + setState((s) => { + switch (event.type) { + case "start": + return { ...s, pages: event.pages }; + case "audit": + return { ...s, events: [...s.events, event.event] }; + case "result": + return { ...s, phase: "done", result: event.result }; + case "error": + return { ...s, phase: "error", error: event.message }; + } + }); + }, []); + + const reset = useCallback(() => { + cancelRef.current(); + setState(EMPTY); + fileRef.current = null; + recordingRef.current = null; + }, []); + + const streamFrom = useCallback( + async (form: FormData) => { + const controller = new AbortController(); + cancelRef.current = () => controller.abort(); + let res: Response; + try { + res = await fetch("/api/run", { method: "POST", body: form, signal: controller.signal }); + } catch { + if (!controller.signal.aborted) apply({ type: "error", message: "Could not reach the server." }); + return; + } + if (!res.ok || !res.body) { + const body = (await res.json().catch(() => ({}))) as { error?: string }; + apply({ type: "error", message: body.error ?? `The server refused the run (${res.status}).` }); + return; + } + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + for (;;) { + const { value, done } = await reader.read().catch(() => ({ value: undefined, done: true })); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + let cut: number; + while ((cut = buffer.indexOf("\n\n")) !== -1) { + const chunk = buffer.slice(0, cut); + buffer = buffer.slice(cut + 2); + if (chunk.startsWith("data: ")) apply(JSON.parse(chunk.slice(6)) as RunEvent); + } + } + }, + [apply], + ); + + const runUpload = useCallback( + (file: File) => { + cancelRef.current(); + fileRef.current = file; + recordingRef.current = null; + setState({ + ...EMPTY, + phase: "running", + source: { kind: "upload", name: file.name }, + pdfUrl: URL.createObjectURL(file), + startedAt: Date.now(), + }); + const form = new FormData(); + form.set("file", file); + void streamFrom(form); + }, + [streamFrom], + ); + + const replay = useCallback( + (events: readonly { at: number; event: RunEvent }[]) => { + const timers: ReturnType[] = []; + for (const { at, event } of events) timers.push(setTimeout(() => apply(event), at)); + cancelRef.current = () => timers.forEach(clearTimeout); + }, + [apply], + ); + + const runGallery = useCallback( + async (id: string) => { + cancelRef.current(); + fileRef.current = null; + const rec = (await fetch(`/gallery/${id}.json`).then((r) => r.json())) as GalleryRecording; + recordingRef.current = rec; + setState({ + ...EMPTY, + phase: "running", + source: { kind: "gallery", id, title: rec.title }, + pdfUrl: `/gallery/${rec.pdf}`, + startedAt: Date.now(), + }); + replay(rec.run); + }, + [replay], + ); + + /** REP-04: answer missing facts and re-validate without another extraction call. */ + const answer = useCallback( + (inputs: readonly UserInput[]) => { + const result = state.result; + if (!result) return; + // The slip keeps its first pass; the second pass is appended below it. + setState((s) => ({ + ...s, + phase: "running", + result: null, + error: null, + priorCostUsd: s.priorCostUsd + Number(result.costUsd), + priorIterations: s.priorIterations + result.iterations, + })); + const rec = recordingRef.current; + if (rec?.followUp) { + replay(rec.followUp); + return; + } + const file = fileRef.current; + if (!file) return; + const form = new FormData(); + form.set("file", file); + form.set("extraction", JSON.stringify(result.extraction)); + form.set("userInputs", JSON.stringify(inputs)); + void streamFrom(form); + }, + [state.result, replay, streamFrom], + ); + + return { + state, + runUpload, + runGallery, + answer, + reset, + galleryAnswer: () => recordingRef.current?.answer ?? null, + }; +} diff --git a/apps/web/lib/verifier.ts b/apps/web/lib/verifier.ts new file mode 100644 index 0000000..d7b7b78 --- /dev/null +++ b/apps/web/lib/verifier.ts @@ -0,0 +1,20 @@ +import "server-only"; +import type { VerifierVerdict } from "@verdict/core"; + +export interface VerifierResponse extends VerifierVerdict { + readonly acceptRecommendation: string; + readonly report: string | null; +} + +export async function validateXRechnung(xml: string): Promise { + const url = process.env.VERIFIER_URL || "http://localhost:8081"; + const secret = process.env.VERIFIER_SECRET; + if (!secret) throw new Error("VERIFIER_SECRET is not configured"); + const res = await fetch(`${url}/v1/validate/xrechnung`, { + method: "POST", + headers: { "X-Verifier-Secret": secret, "Content-Type": "application/xml" }, + body: xml, + }); + if (!res.ok) throw new Error(`verifier responded ${res.status}`); + return (await res.json()) as VerifierResponse; +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 0000000..ce4e94a --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,7 @@ +/// +/// +import "./.next/types/routes.d.ts"; +import "./.next/types/root-params.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts new file mode 100644 index 0000000..6b5fe8b --- /dev/null +++ b/apps/web/next.config.ts @@ -0,0 +1,12 @@ +import type { NextConfig } from "next"; + +const config: NextConfig = { + // The core package ships TypeScript sources with .ts import specifiers. + transpilePackages: ["@verdict/core"], + // pdf.js reads its font data from node_modules at runtime on the server. + serverExternalPackages: ["pdfjs-dist"], + output: "standalone", + poweredByHeader: false, +}; + +export default config; diff --git a/apps/web/package.json b/apps/web/package.json index f422e06..ad14cfa 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -3,7 +3,33 @@ "private": true, "type": "module", "scripts": { - "typecheck": "tsc -p tsconfig.json", - "test": "echo 'web: no tests yet (UI starts in phase 4)'" + "dev": "next dev --port 3000", + "build": "next build", + "start": "next start --port ${PORT:-3000}", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run --passWithNoTests", + "gallery:build": "tsx --env-file-if-exists=../../.env scripts/build-gallery.ts", + "predev": "node scripts/copy-pdf-worker.mjs", + "prebuild": "node scripts/copy-pdf-worker.mjs" + }, + "dependencies": { + "@fontsource/barlow": "^5.3.0", + "@fontsource/barlow-condensed": "^5.3.0", + "@fontsource/jetbrains-mono": "^5.3.0", + "@verdict/core": "workspace:*", + "decimal.js": "^10.6.0", + "lucide-react": "^1.50.0", + "next": "^16.3.8", + "pdfjs-dist": "^6.3.289", + "react": "^19.3.0", + "react-dom": "^19.3.0", + "server-only": "^0.0.1" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "tailwindcss": "^4.3.3", + "tsx": "^4.23.15" } } diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs new file mode 100644 index 0000000..a9b5c43 --- /dev/null +++ b/apps/web/postcss.config.mjs @@ -0,0 +1 @@ +export default { plugins: { "@tailwindcss/postcss": {} } }; diff --git a/apps/web/public/gallery/clean.json b/apps/web/public/gallery/clean.json new file mode 100644 index 0000000..f06d9dc --- /dev/null +++ b/apps/web/public/gallery/clean.json @@ -0,0 +1 @@ +{"id":"clean","title":"A clean invoice","story":"Every field quoted from the page, totals computed, KoSIT accepts it.","pdf":"clean.pdf","synthetic":true,"run":[{"at":203,"event":{"type":"start","pages":["Nordwind Fachverlag GmbH\nNordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE\nStadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer 123456XX\nRechnungsdatum 04.04.2016\nLeitweg-ID / Käuferreferenz 04011000-12345-03\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €\n2 Porto + Versandkosten 1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €\nSumme netto 314,86 €\nUSt 7 % (S) auf 314,86 € 22,04 €\nUmsatzsteuer 22,04 €\nRechnungsbetrag 336,90 €\nZahlungsbedingungen\nZahlbar sofort ohne Abzug.\nNordwind Fachverlag GmbH\nLindenallee 47\n12345 Tannenberg\nDE\nAnsprechpartner: Paul Neumann\nTelefon: +49 1234-5678\nE-Mail: seller@email.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nUSt-IdNr.: DE 123456789\nHandelsregister: HRA 1172 Amtsgericht Eichstedt\nSeite 1 von 1"],"uploadHash":"sha256:644484762fe51e865560249c9e1b856d61b558782bd48c7e6fbc95042e955547"}},{"at":203,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:55:15.207Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:644484762fe51e865560249c9e1b856d61b558782bd48c7e6fbc95042e955547","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:738775189512202c33c2e24214ef690beac170cc3f05c04e68057c003ec3fdd3"}}},{"at":6320,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:55:21.324Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:7ed0b63d06bd632f8930c1f5c28b1d2c8e53e5a451a8c82c87a2fa257081a04f","promptTokens":1257,"completionTokens":1529,"costUsd":"0.00175320","latencyMs":6111},"prevHash":"sha256:738775189512202c33c2e24214ef690beac170cc3f05c04e68057c003ec3fdd3","hash":"sha256:6632de07cfc15ccbd216e028b10437457a75713752a76747f8be611fdb6473b0"}}},{"at":6328,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:55:21.332Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":46,"rejected":[]},"prevHash":"sha256:6632de07cfc15ccbd216e028b10437457a75713752a76747f8be611fdb6473b0","hash":"sha256:ad88df91858a8539ec9726c17ed7a1a0ddc1f9b4959512adf383199c39794a33"}}},{"at":6328,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:55:21.333Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":2,"vatGroups":1},"prevHash":"sha256:ad88df91858a8539ec9726c17ed7a1a0ddc1f9b4959512adf383199c39794a33","hash":"sha256:a298227a07764eddbaa37d3c74466d29c93843d8e3a8f5a8349caf12bb230f2a"}}},{"at":6328,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:55:21.333Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:a298227a07764eddbaa37d3c74466d29c93843d8e3a8f5a8349caf12bb230f2a","hash":"sha256:229cd04094133f69f1338a35f9b282ca8fa3204fca98b2b7911b83328c8cbdd9"}}},{"at":6329,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:55:21.333Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24","data":{},"prevHash":"sha256:229cd04094133f69f1338a35f9b282ca8fa3204fca98b2b7911b83328c8cbdd9","hash":"sha256:661ad73257da0142f1866a4fb40793058ae8c0d99dcb6a6b627562a3b491a6cb"}}},{"at":6364,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T20:55:21.368Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24","data":{"valid":true,"errorCount":0,"reportHash":"17b9c60953686221d3d3f340f85181edeac7003bc29d331f83c98e09e9d857b0"},"prevHash":"sha256:661ad73257da0142f1866a4fb40793058ae8c0d99dcb6a6b627562a3b491a6cb","hash":"sha256:1ecb0a69ef2e2e5ac8925bb7c17bfdadac17073e6459300a9d5c0e20f5a492c3"}}},{"at":6364,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T20:55:21.368Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24"},"fieldsWithoutProvenance":0},"prevHash":"sha256:1ecb0a69ef2e2e5ac8925bb7c17bfdadac17073e6459300a9d5c0e20f5a492c3","hash":"sha256:1585f305b26275689039dc488d1a05bf8cbe3d7735648e9e4e3c6ca4b58ad03e"}}},{"at":6364,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"123456XX","issueDate":"2016-04-04","typeCode":"380","currency":"EUR","buyerReference":"04011000-12345-03","paymentTerms":"Zahlbar sofort ohne Abzug.","seller":{"name":"Nordwind Fachverlag GmbH","legalRegistrationId":"HRA 1172 Amtsgericht Eichstedt","vatId":"DE 123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Lindenallee 47","city":"Tannenberg","postcode":"12345","countryCode":"DE"},"contact":{"name":"Paul Neumann","phone":"+49 1234-5678","email":"seller@email.de"}},"buyer":{"name":"Stadtverwaltung Birkenfeld","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Am Markt 3","city":"Eichstedt","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"1","name":"Zeitschrift Seminar: Vergaberecht kompakt","quantity":"1","unitCode":"XPP","netPrice":"288.79","vatCategory":"S","vatRate":"7"},{"id":"2","name":"Porto + Versandkosten","quantity":"1","unitCode":"XPP","netPrice":"26.07","vatCategory":"S","vatRate":"7"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH\nNordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer 123456XX"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 04.04.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 04011000-12345-03"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlbar sofort ohne Abzug."}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE 123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Paul Neumann"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: +49 1234-5678"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: seller@email.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 314,86 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 7 % (S) auf 314,86 € 22,04 €\nUmsatzsteuer 22,04 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 336,90 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"17b9c60953686221d3d3f340f85181edeac7003bc29d331f83c98e09e9d857b0","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"314.86","taxTotal":"22.04","grandTotal":"336.90"},"documentTotals":{"lineTotal":"314.86","taxTotal":"22.04","grandTotal":"336.90","lines":["288.79","26.07"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n 123456XX\n 380\n \n 20160404\n \n \n \n \n \n 1\n \n \n Zeitschrift Seminar: Vergaberecht kompakt\n \n \n \n 288.79\n \n \n \n 1\n \n \n \n VAT\n S\n 7\n \n \n 288.79\n \n \n \n \n \n 2\n \n \n Porto + Versandkosten\n \n \n \n 26.07\n \n \n \n 1\n \n \n \n VAT\n S\n 7\n \n \n 26.07\n \n \n \n \n 04011000-12345-03\n \n Nordwind Fachverlag GmbH\n \n HRA 1172 Amtsgericht Eichstedt\n \n \n Paul Neumann\n \n +49 1234-5678\n \n \n seller@email.de\n \n \n \n 12345\n Lindenallee 47\n Tannenberg\n DE\n \n \n seller@email.de\n \n \n DE 123456789\n \n \n \n Stadtverwaltung Birkenfeld\n \n 12345\n Am Markt 3\n Eichstedt\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n \n \n \n 22.04\n VAT\n 314.86\n S\n 7\n \n \n Zahlbar sofort ohne Abzug.\n \n \n 314.86\n 314.86\n 22.04\n 336.90\n 336.90\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:55:15.207Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:644484762fe51e865560249c9e1b856d61b558782bd48c7e6fbc95042e955547\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:738775189512202c33c2e24214ef690beac170cc3f05c04e68057c003ec3fdd3\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:55:21.324Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:7ed0b63d06bd632f8930c1f5c28b1d2c8e53e5a451a8c82c87a2fa257081a04f\",\"promptTokens\":1257,\"completionTokens\":1529,\"costUsd\":\"0.00175320\",\"latencyMs\":6111},\"prevHash\":\"sha256:738775189512202c33c2e24214ef690beac170cc3f05c04e68057c003ec3fdd3\",\"hash\":\"sha256:6632de07cfc15ccbd216e028b10437457a75713752a76747f8be611fdb6473b0\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:55:21.332Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":46,\"rejected\":[]},\"prevHash\":\"sha256:6632de07cfc15ccbd216e028b10437457a75713752a76747f8be611fdb6473b0\",\"hash\":\"sha256:ad88df91858a8539ec9726c17ed7a1a0ddc1f9b4959512adf383199c39794a33\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:55:21.333Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":2,\"vatGroups\":1},\"prevHash\":\"sha256:ad88df91858a8539ec9726c17ed7a1a0ddc1f9b4959512adf383199c39794a33\",\"hash\":\"sha256:a298227a07764eddbaa37d3c74466d29c93843d8e3a8f5a8349caf12bb230f2a\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:55:21.333Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:a298227a07764eddbaa37d3c74466d29c93843d8e3a8f5a8349caf12bb230f2a\",\"hash\":\"sha256:229cd04094133f69f1338a35f9b282ca8fa3204fca98b2b7911b83328c8cbdd9\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:55:21.333Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\",\"data\":{},\"prevHash\":\"sha256:229cd04094133f69f1338a35f9b282ca8fa3204fca98b2b7911b83328c8cbdd9\",\"hash\":\"sha256:661ad73257da0142f1866a4fb40793058ae8c0d99dcb6a6b627562a3b491a6cb\"}\n{\"seq\":6,\"ts\":\"2026-10-02T20:55:21.368Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"17b9c60953686221d3d3f340f85181edeac7003bc29d331f83c98e09e9d857b0\"},\"prevHash\":\"sha256:661ad73257da0142f1866a4fb40793058ae8c0d99dcb6a6b627562a3b491a6cb\",\"hash\":\"sha256:1ecb0a69ef2e2e5ac8925bb7c17bfdadac17073e6459300a9d5c0e20f5a492c3\"}\n{\"seq\":7,\"ts\":\"2026-10-02T20:55:21.368Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:1ecb0a69ef2e2e5ac8925bb7c17bfdadac17073e6459300a9d5c0e20f5a492c3\",\"hash\":\"sha256:1585f305b26275689039dc488d1a05bf8cbe3d7735648e9e4e3c6ca4b58ad03e\"}\n","iterations":0,"costUsd":"0.001753","extraction":{"header":{"invoiceNumber":{"value":"123456XX","quote":"Rechnungsnummer 123456XX","page":1},"issueDate":{"value":"04.04.2016","quote":"Rechnungsdatum 04.04.2016","page":1},"dueDate":null,"buyerReference":{"value":"04011000-12345-03","quote":"Leitweg-ID / Käuferreferenz 04011000-12345-03","page":1},"orderReference":null,"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Zahlbar sofort ohne Abzug.","quote":"Zahlbar sofort ohne Abzug.","page":1}},"seller":{"name":{"value":"Nordwind Fachverlag GmbH","quote":"Nordwind Fachverlag GmbH\nNordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"street":{"value":"Lindenallee 47","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"addressLine2":null,"city":{"value":"Tannenberg","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"postcode":{"value":"12345","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"country":{"value":"DE","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":null,"legalRegistrationId":{"value":"HRA 1172 Amtsgericht Eichstedt","quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt","page":1},"vatId":{"value":"DE 123456789","quote":"USt-IdNr.: DE 123456789","page":1},"taxNumber":null,"contactName":{"value":"Paul Neumann","quote":"Ansprechpartner: Paul Neumann","page":1},"contactPhone":{"value":"+49 1234-5678","quote":"Telefon: +49 1234-5678","page":1},"contactEmail":{"value":"seller@email.de","quote":"E-Mail: seller@email.de","page":1}},"buyer":{"name":{"value":"Stadtverwaltung Birkenfeld","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"street":{"value":"Am Markt 3","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"addressLine2":null,"city":{"value":"Eichstedt","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"postcode":{"value":"12345","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"country":{"value":"DE","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":null},"vatExemptions":[],"lines":[{"rowQuote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €","page":1,"lineId":"1","name":"Zeitschrift Seminar:\nVergaberecht kompakt","quantity":"1","unit":"Stk. (XPP)","netPrice":"288,79 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"288,79 €"},{"rowQuote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €","page":1,"lineId":"2","name":"Porto + Versandkosten","quantity":"1","unit":"Stk. (XPP)","netPrice":"26,07 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"26,07 €"}],"documentTotals":{"lineTotal":{"value":"314,86 €","quote":"Summe netto 314,86 €","page":1},"taxTotal":{"value":"22,04 €","quote":"USt 7 % (S) auf 314,86 € 22,04 €\nUmsatzsteuer 22,04 €","page":1},"grandTotal":{"value":"336,90 €","quote":"Rechnungsbetrag 336,90 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/clean.pdf b/apps/web/public/gallery/clean.pdf new file mode 100644 index 0000000..75d699c Binary files /dev/null and b/apps/web/public/gallery/clean.pdf differ diff --git a/apps/web/public/gallery/index.json b/apps/web/public/gallery/index.json new file mode 100644 index 0000000..1e051a1 --- /dev/null +++ b/apps/web/public/gallery/index.json @@ -0,0 +1,49 @@ +{ + "generatedAt": "2026-10-02", + "cases": [ + { + "id": "clean", + "title": "A clean invoice", + "story": "Every field quoted from the page, totals computed, KoSIT accepts it.", + "state": "OUTPUT" + }, + { + "id": "seven-lines", + "title": "Seven line items", + "story": "Seven line items. Nemotron Ultra repairs three fields behind the patch guard; every amount is computed from the lines.", + "state": "OUTPUT" + }, + { + "id": "noisy-layout", + "title": "Unusual layout", + "story": "Plain number format, ISO dates and shuffled blocks still end in a valid e-invoice.", + "state": "OUTPUT" + }, + { + "id": "unit-word", + "title": "Units printed as words", + "story": "\"Stück\" is not a unit code. KoSIT reports it as an error yet still accepts the invoice; Verdict normalizes it to H87.", + "state": "OUTPUT" + }, + { + "id": "total-off", + "title": "Printed total is wrong", + "story": "The document adds up to 1.00 less than it claims. Verdict trusts the lines, computes the total and flags the difference.", + "state": "OUTPUT" + }, + { + "id": "missing-leitweg", + "title": "Leitweg-ID missing", + "story": "The buyer reference is not printed. Verdict asks for it instead of inventing one.", + "state": "NEEDS_INPUT", + "followUpState": "OUTPUT" + }, + { + "id": "missing-iban", + "title": "Bank details missing", + "story": "Payment by transfer, but no IBAN on the page. Verdict stops and asks.", + "state": "NEEDS_INPUT", + "followUpState": "OUTPUT" + } + ] +} diff --git a/apps/web/public/gallery/missing-iban.json b/apps/web/public/gallery/missing-iban.json new file mode 100644 index 0000000..bc27c03 --- /dev/null +++ b/apps/web/public/gallery/missing-iban.json @@ -0,0 +1 @@ +{"id":"missing-iban","title":"Bank details missing","story":"Payment by transfer, but no IBAN on the page. Verdict stops and asks.","pdf":"missing-iban.pdf","synthetic":true,"run":[{"at":246,"event":{"type":"start","pages":["Brückner & Söhne Haustechnik KG\nBrückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE\nLandesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer R123456789\nRechnungsdatum 18.01.2016\nFälligkeitsdatum 01.02.2016\nLeitweg-ID / Käuferreferenz 04011000-12349-88\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €\nSumme netto 2.374,68 €\nUSt 19 % (S) auf 2.374,68 € 451,19 €\nUmsatzsteuer 451,19 €\nRechnungsbetrag 2.825,87 €\nZahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo\nBrückner & Söhne Haustechnik KG\nPostfach 123456\n12345 Musterstadt\nDE\nAnsprechpartner: Auskunft erteilt Ihnen\nTelefon: 1234567890\nE-Mail: seller@test.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nUSt-IdNr.: DE123456789\nHandelsregister: HRB 123\nSeite 1 von 1"],"uploadHash":"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891"}},{"at":246,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T21:23:52.485Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:d2ef3cfae80fcd9d9a7dcd8349a03b4f7b326fe4f348b1bb6d053faa1d59fcc8"}}},{"at":6318,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T21:23:58.557Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:68dd825895cf87ffcd89db402083b49303e646c58b0a589395d8a4d3f2bd088b","promptTokens":1274,"completionTokens":1563,"costUsd":"0.00178890","latencyMs":6067},"prevHash":"sha256:d2ef3cfae80fcd9d9a7dcd8349a03b4f7b326fe4f348b1bb6d053faa1d59fcc8","hash":"sha256:8e28f0c2aa9092f679b705821495438fac3456514022e64c133a0618621c6c09"}}},{"at":6326,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T21:23:58.565Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":37,"rejected":[]},"prevHash":"sha256:8e28f0c2aa9092f679b705821495438fac3456514022e64c133a0618621c6c09","hash":"sha256:340cfbcf46f4bb459796ccb471189e0d9caaa8bd7e2a4ef7845cf2f76004627a"}}},{"at":6326,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T21:23:58.565Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":1,"vatGroups":1},"prevHash":"sha256:340cfbcf46f4bb459796ccb471189e0d9caaa8bd7e2a4ef7845cf2f76004627a","hash":"sha256:bca7baca956e9f4b9e25c7c2b2517eae1029050e86b11540f8b28b58b2a64b15"}}},{"at":6326,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T21:23:58.565Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:bca7baca956e9f4b9e25c7c2b2517eae1029050e86b11540f8b28b58b2a64b15","hash":"sha256:3cb2d0ef8edc43b19d878a73a397a58fc0eeccee5a3911c7d55abe74873e30e1"}}},{"at":6327,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T21:23:58.566Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:bc69d031c6490a4fdcdab125ef11c8f43d8ffcaf616e8f405306a07fd1675abd","data":{},"prevHash":"sha256:3cb2d0ef8edc43b19d878a73a397a58fc0eeccee5a3911c7d55abe74873e30e1","hash":"sha256:a1bfd290597e8b5438f4e9b1d3b08024ab5c062cad9f489f5b9bbdf44f0826a4"}}},{"at":6398,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T21:23:58.637Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:bc69d031c6490a4fdcdab125ef11c8f43d8ffcaf616e8f405306a07fd1675abd","data":{"valid":false,"errorCount":1,"reportHash":"b5f30f4dfaf515cb5df4f3b177f23558a88d05bd131ce1c06a37652d29c96328"},"prevHash":"sha256:a1bfd290597e8b5438f4e9b1d3b08024ab5c062cad9f489f5b9bbdf44f0826a4","hash":"sha256:c7a2618c20d101e8438be3348a41ae0f911d60006f79904d339f067b0d264d69"}}},{"at":7274,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T21:23:59.513Z","type":"REPAIR_PLAN","actor":{"kind":"model","model":"nvidia/Nemotron-3-Ultra-550b-a55b"},"data":{"promptHash":"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad","promptTokens":1274,"completionTokens":134,"costUsd":"0.00167600","latencyMs":875},"prevHash":"sha256:c7a2618c20d101e8438be3348a41ae0f911d60006f79904d339f067b0d264d69","hash":"sha256:c7f33df80d5e7da4e26f969dea5c41d1fdf4f154b20d01f4a708d0fb9fd7881f"}}},{"at":7275,"event":{"type":"audit","event":{"seq":8,"ts":"2026-10-02T21:23:59.514Z","type":"PATCH_GUARD","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":[],"rejected":[],"unresolvable":["[BR-DE-1] [BR-DE-1] Eine Rechnung (INVOICE) muss Angaben zu \"PAYMENT INSTRUCTIONS\" (BG-16) enthalten. (at /Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1])"]},"prevHash":"sha256:c7f33df80d5e7da4e26f969dea5c41d1fdf4f154b20d01f4a708d0fb9fd7881f","hash":"sha256:6705cd21fa22f5a1a6770509639a15387e2aae9f6d63522fdd2eb575664937f6"}}},{"at":7275,"event":{"type":"audit","event":{"seq":9,"ts":"2026-10-02T21:23:59.514Z","type":"NEEDS_INPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"missing":["BT-84"],"openIssues":["BR-DE-1"]},"prevHash":"sha256:6705cd21fa22f5a1a6770509639a15387e2aae9f6d63522fdd2eb575664937f6","hash":"sha256:6afb2735b78915b94b029b02aa59d88b3773e0ee491f1c3fd0b3810d60714f4d"}}},{"at":7275,"event":{"type":"result","result":{"state":"NEEDS_INPUT","invoice":{"number":"R123456789","issueDate":"2016-01-18","typeCode":"380","currency":"EUR","dueDate":"2016-02-01","buyerReference":"04011000-12349-88","paymentTerms":"Die Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo","seller":{"name":"Brückner & Söhne Haustechnik KG","legalRegistrationId":"HRB 123","vatId":"DE123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Postfach 123456","city":"Musterstadt","postcode":"12345","countryCode":"DE"},"contact":{"name":"Auskunft erteilt Ihnen","phone":"1234567890","email":"seller@test.de"}},"buyer":{"name":"Landesamt für Statistik Musterland","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Lindenallee 47","city":"Tannenberg","postcode":"12345","countryCode":"DE"}},"vatExemptions":[],"lines":[{"id":"0020","name":"Zentrale Administrationslösung Bereitstellung von Werkzeugen zur Administration/Betreuung","quantity":"12","unitCode":"XPP","netPrice":"197.89","vatCategory":"S","vatRate":"19"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer R123456789"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 18.01.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["dueDate",{"kind":"evidence","page":1,"quote":"Fälligkeitsdatum 01.02.2016"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 04011000-12349-88"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo"}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRB 123"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Auskunft erteilt Ihnen"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: 1234567890"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: seller@test.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 2.374,68 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 19 % (S) auf 2.374,68 € 451,19 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 2.825,87 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}]],"missing":[{"path":"payment.iban","bt":"BT-84","reason":"required by BR-DE-1 but not on the document"}],"openIssues":[{"id":"BR-DE-1","message":"[BR-DE-1] Eine Rechnung (INVOICE) muss Angaben zu \"PAYMENT INSTRUCTIONS\" (BG-16) enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]"}],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":false,"errors":[{"ruleId":"BR-DE-1","severity":"error","message":"[BR-DE-1] Eine Rechnung (INVOICE) muss Angaben zu \"PAYMENT INSTRUCTIONS\" (BG-16) enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]"},{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"b5f30f4dfaf515cb5df4f3b177f23558a88d05bd131ce1c06a37652d29c96328","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":null,"documentTotals":{"lineTotal":"2374.68","taxTotal":"451.19","grandTotal":"2825.87","lines":["2374.68"]},"xml":null,"auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T21:23:52.485Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:d2ef3cfae80fcd9d9a7dcd8349a03b4f7b326fe4f348b1bb6d053faa1d59fcc8\"}\n{\"seq\":1,\"ts\":\"2026-10-02T21:23:58.557Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:68dd825895cf87ffcd89db402083b49303e646c58b0a589395d8a4d3f2bd088b\",\"promptTokens\":1274,\"completionTokens\":1563,\"costUsd\":\"0.00178890\",\"latencyMs\":6067},\"prevHash\":\"sha256:d2ef3cfae80fcd9d9a7dcd8349a03b4f7b326fe4f348b1bb6d053faa1d59fcc8\",\"hash\":\"sha256:8e28f0c2aa9092f679b705821495438fac3456514022e64c133a0618621c6c09\"}\n{\"seq\":2,\"ts\":\"2026-10-02T21:23:58.565Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":37,\"rejected\":[]},\"prevHash\":\"sha256:8e28f0c2aa9092f679b705821495438fac3456514022e64c133a0618621c6c09\",\"hash\":\"sha256:340cfbcf46f4bb459796ccb471189e0d9caaa8bd7e2a4ef7845cf2f76004627a\"}\n{\"seq\":3,\"ts\":\"2026-10-02T21:23:58.565Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":1,\"vatGroups\":1},\"prevHash\":\"sha256:340cfbcf46f4bb459796ccb471189e0d9caaa8bd7e2a4ef7845cf2f76004627a\",\"hash\":\"sha256:bca7baca956e9f4b9e25c7c2b2517eae1029050e86b11540f8b28b58b2a64b15\"}\n{\"seq\":4,\"ts\":\"2026-10-02T21:23:58.565Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:bca7baca956e9f4b9e25c7c2b2517eae1029050e86b11540f8b28b58b2a64b15\",\"hash\":\"sha256:3cb2d0ef8edc43b19d878a73a397a58fc0eeccee5a3911c7d55abe74873e30e1\"}\n{\"seq\":5,\"ts\":\"2026-10-02T21:23:58.566Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:bc69d031c6490a4fdcdab125ef11c8f43d8ffcaf616e8f405306a07fd1675abd\",\"data\":{},\"prevHash\":\"sha256:3cb2d0ef8edc43b19d878a73a397a58fc0eeccee5a3911c7d55abe74873e30e1\",\"hash\":\"sha256:a1bfd290597e8b5438f4e9b1d3b08024ab5c062cad9f489f5b9bbdf44f0826a4\"}\n{\"seq\":6,\"ts\":\"2026-10-02T21:23:58.637Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:bc69d031c6490a4fdcdab125ef11c8f43d8ffcaf616e8f405306a07fd1675abd\",\"data\":{\"valid\":false,\"errorCount\":1,\"reportHash\":\"b5f30f4dfaf515cb5df4f3b177f23558a88d05bd131ce1c06a37652d29c96328\"},\"prevHash\":\"sha256:a1bfd290597e8b5438f4e9b1d3b08024ab5c062cad9f489f5b9bbdf44f0826a4\",\"hash\":\"sha256:c7a2618c20d101e8438be3348a41ae0f911d60006f79904d339f067b0d264d69\"}\n{\"seq\":7,\"ts\":\"2026-10-02T21:23:59.513Z\",\"type\":\"REPAIR_PLAN\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/Nemotron-3-Ultra-550b-a55b\"},\"data\":{\"promptHash\":\"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad\",\"promptTokens\":1274,\"completionTokens\":134,\"costUsd\":\"0.00167600\",\"latencyMs\":875},\"prevHash\":\"sha256:c7a2618c20d101e8438be3348a41ae0f911d60006f79904d339f067b0d264d69\",\"hash\":\"sha256:c7f33df80d5e7da4e26f969dea5c41d1fdf4f154b20d01f4a708d0fb9fd7881f\"}\n{\"seq\":8,\"ts\":\"2026-10-02T21:23:59.514Z\",\"type\":\"PATCH_GUARD\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":[],\"rejected\":[],\"unresolvable\":[\"[BR-DE-1] [BR-DE-1] Eine Rechnung (INVOICE) muss Angaben zu \\\"PAYMENT INSTRUCTIONS\\\" (BG-16) enthalten. (at /Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1])\"]},\"prevHash\":\"sha256:c7f33df80d5e7da4e26f969dea5c41d1fdf4f154b20d01f4a708d0fb9fd7881f\",\"hash\":\"sha256:6705cd21fa22f5a1a6770509639a15387e2aae9f6d63522fdd2eb575664937f6\"}\n{\"seq\":9,\"ts\":\"2026-10-02T21:23:59.514Z\",\"type\":\"NEEDS_INPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"missing\":[\"BT-84\"],\"openIssues\":[\"BR-DE-1\"]},\"prevHash\":\"sha256:6705cd21fa22f5a1a6770509639a15387e2aae9f6d63522fdd2eb575664937f6\",\"hash\":\"sha256:6afb2735b78915b94b029b02aa59d88b3773e0ee491f1c3fd0b3810d60714f4d\"}\n","iterations":1,"costUsd":"0.003465","extraction":{"header":{"invoiceNumber":{"value":"R123456789","quote":"Rechnungsnummer R123456789","page":1},"issueDate":{"value":"18.01.2016","quote":"Rechnungsdatum 18.01.2016","page":1},"dueDate":{"value":"01.02.2016","quote":"Fälligkeitsdatum 01.02.2016","page":1},"buyerReference":{"value":"04011000-12349-88","quote":"Leitweg-ID / Käuferreferenz 04011000-12349-88","page":1},"orderReference":{"value":"","quote":"","page":1},"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Die Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo\n","quote":"Zahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo","page":1}},"seller":{"name":{"value":"Brückner & Söhne Haustechnik KG","quote":"Brückner & Söhne Haustechnik KG","page":1},"street":{"value":"Postfach 123456","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Musterstadt","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"postcode":{"value":"12345","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"country":{"value":"DE","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":{"value":"","quote":"","page":1},"legalRegistrationId":{"value":"HRB 123","quote":"Handelsregister: HRB 123","page":1},"vatId":{"value":"DE123456789","quote":"USt-IdNr.: DE123456789","page":1},"taxNumber":{"value":"","quote":"","page":1},"contactName":{"value":"Auskunft erteilt Ihnen","quote":"Ansprechpartner: Auskunft erteilt Ihnen","page":1},"contactPhone":{"value":"1234567890","quote":"Telefon: 1234567890","page":1},"contactEmail":{"value":"seller@test.de","quote":"E-Mail: seller@test.de","page":1}},"buyer":{"name":{"value":"Landesamt für Statistik Musterland","quote":"Landesamt für Statistik Musterland","page":1},"street":{"value":"Lindenallee 47","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Tannenberg","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"postcode":{"value":"12345","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"country":{"value":"DE","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"","quote":"","page":1},"accountName":{"value":"","quote":"","page":1}},"vatExemptions":[],"lines":[{"rowQuote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €","page":1,"lineId":"0020","name":"Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung","quantity":"12","unit":"Stk. (XPP)","netPrice":"197,89","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"2.374,68"}],"documentTotals":{"lineTotal":{"value":"2.374,68 €","quote":"Summe netto 2.374,68 €","page":1},"taxTotal":{"value":"451,19 €","quote":"USt 19 % (S) auf 2.374,68 € 451,19 €","page":1},"grandTotal":{"value":"2.825,87 €","quote":"Rechnungsbetrag 2.825,87 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}],"answer":[{"path":"payment.iban","value":"DE02 1203 0000 0000 2020 51"}],"followUp":[{"at":10,"event":{"type":"start","pages":["Brückner & Söhne Haustechnik KG\nBrückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE\nLandesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer R123456789\nRechnungsdatum 18.01.2016\nFälligkeitsdatum 01.02.2016\nLeitweg-ID / Käuferreferenz 04011000-12349-88\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €\nSumme netto 2.374,68 €\nUSt 19 % (S) auf 2.374,68 € 451,19 €\nUmsatzsteuer 451,19 €\nRechnungsbetrag 2.825,87 €\nZahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo\nBrückner & Söhne Haustechnik KG\nPostfach 123456\n12345 Musterstadt\nDE\nAnsprechpartner: Auskunft erteilt Ihnen\nTelefon: 1234567890\nE-Mail: seller@test.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nUSt-IdNr.: DE123456789\nHandelsregister: HRB 123\nSeite 1 von 1"],"uploadHash":"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891"}},{"at":10,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T21:23:59.524Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:341c99eeba3517969a4f9777388a40e44f0d47884e5899707cf32ba800b8a0eb"}}},{"at":10,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T21:23:59.524Z","type":"EXTRACT","actor":{"kind":"core","version":"0.1.0"},"data":{"reused":true},"prevHash":"sha256:341c99eeba3517969a4f9777388a40e44f0d47884e5899707cf32ba800b8a0eb","hash":"sha256:4f385509d9af5f13a8c59aeac7e58c3f0d5751a785390d1e9fef7ed93f86a719"}}},{"at":10,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T21:23:59.525Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":37,"rejected":[]},"prevHash":"sha256:4f385509d9af5f13a8c59aeac7e58c3f0d5751a785390d1e9fef7ed93f86a719","hash":"sha256:c2e742699954aaf1f7f45201b9952fb588ac3dc9e7a8df39b80bf0dffd044822"}}},{"at":11,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T21:23:59.525Z","type":"USER_INPUT","actor":{"kind":"user"},"data":{"fields":["BT-84"]},"prevHash":"sha256:c2e742699954aaf1f7f45201b9952fb588ac3dc9e7a8df39b80bf0dffd044822","hash":"sha256:2bfae8b4200cee06aedbe59ed8a8a0b61fb8275a4200dab0a7fef91dcd971705"}}},{"at":11,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T21:23:59.525Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":1,"vatGroups":1},"prevHash":"sha256:2bfae8b4200cee06aedbe59ed8a8a0b61fb8275a4200dab0a7fef91dcd971705","hash":"sha256:1ba86339bb9dae7c0d4a2a33a4a27bf9d3ca822051b64e19eca8905394e7f9e2"}}},{"at":11,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T21:23:59.525Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:1ba86339bb9dae7c0d4a2a33a4a27bf9d3ca822051b64e19eca8905394e7f9e2","hash":"sha256:db446ed13e6e5dbaff14bcca2719e352d5beefaac1decab7009e84d638c3e55a"}}},{"at":11,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T21:23:59.525Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270","data":{},"prevHash":"sha256:db446ed13e6e5dbaff14bcca2719e352d5beefaac1decab7009e84d638c3e55a","hash":"sha256:76c4012de835320e9d8dd65a340cff9563552f8858de383ef930ccbc56de4eba"}}},{"at":39,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T21:23:59.553Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270","data":{"valid":true,"errorCount":0,"reportHash":"68890637522a6d3ec7e922a397c0dcaeafad86e18a45ae102bb593f759010983"},"prevHash":"sha256:76c4012de835320e9d8dd65a340cff9563552f8858de383ef930ccbc56de4eba","hash":"sha256:fc878eef9e874dafd2240f1fb209252512fc599d6d8c13dc41219acd99d161bb"}}},{"at":39,"event":{"type":"audit","event":{"seq":8,"ts":"2026-10-02T21:23:59.553Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270"},"fieldsWithoutProvenance":0},"prevHash":"sha256:fc878eef9e874dafd2240f1fb209252512fc599d6d8c13dc41219acd99d161bb","hash":"sha256:73583c0cb545563ff17448281201c4d056cceb1ccc8b6bbadf42186ef1b5fb42"}}},{"at":39,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"R123456789","issueDate":"2016-01-18","typeCode":"380","currency":"EUR","dueDate":"2016-02-01","buyerReference":"04011000-12349-88","paymentTerms":"Die Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo","seller":{"name":"Brückner & Söhne Haustechnik KG","legalRegistrationId":"HRB 123","vatId":"DE123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Postfach 123456","city":"Musterstadt","postcode":"12345","countryCode":"DE"},"contact":{"name":"Auskunft erteilt Ihnen","phone":"1234567890","email":"seller@test.de"}},"buyer":{"name":"Landesamt für Statistik Musterland","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Lindenallee 47","city":"Tannenberg","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE02120300000000202051"},"vatExemptions":[],"lines":[{"id":"0020","name":"Zentrale Administrationslösung Bereitstellung von Werkzeugen zur Administration/Betreuung","quantity":"12","unitCode":"XPP","netPrice":"197.89","vatCategory":"S","vatRate":"19"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer R123456789"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 18.01.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["dueDate",{"kind":"evidence","page":1,"quote":"Fälligkeitsdatum 01.02.2016"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 04011000-12349-88"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo"}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRB 123"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Auskunft erteilt Ihnen"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: 1234567890"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: seller@test.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE"}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 2.374,68 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 19 % (S) auf 2.374,68 € 451,19 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 2.825,87 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €"}],["payment.iban",{"kind":"user","enteredAt":"2026-10-02T21:23:59.525Z"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"68890637522a6d3ec7e922a397c0dcaeafad86e18a45ae102bb593f759010983","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"2374.68","taxTotal":"451.19","grandTotal":"2825.87"},"documentTotals":{"lineTotal":"2374.68","taxTotal":"451.19","grandTotal":"2825.87","lines":["2374.68"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n R123456789\n 380\n \n 20160118\n \n \n \n \n \n 0020\n \n \n Zentrale Administrationslösung Bereitstellung von Werkzeugen zur Administration/Betreuung\n \n \n \n 197.89\n \n \n \n 12\n \n \n \n VAT\n S\n 19\n \n \n 2374.68\n \n \n \n \n 04011000-12349-88\n \n Brückner & Söhne Haustechnik KG\n \n HRB 123\n \n \n Auskunft erteilt Ihnen\n \n 1234567890\n \n \n seller@test.de\n \n \n \n 12345\n Postfach 123456\n Musterstadt\n DE\n \n \n seller@email.de\n \n \n DE123456789\n \n \n \n Landesamt für Statistik Musterland\n \n 12345\n Lindenallee 47\n Tannenberg\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE02120300000000202051\n \n \n \n 451.19\n VAT\n 2374.68\n S\n 19\n \n \n Die Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo\n \n 20160201\n \n \n \n 2374.68\n 2374.68\n 451.19\n 2825.87\n 2825.87\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T21:23:59.524Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:04f5c4dcb9ede4c4590e14a17de61f7c0364f22d158b911bdea75e39ff7d2891\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:341c99eeba3517969a4f9777388a40e44f0d47884e5899707cf32ba800b8a0eb\"}\n{\"seq\":1,\"ts\":\"2026-10-02T21:23:59.524Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"reused\":true},\"prevHash\":\"sha256:341c99eeba3517969a4f9777388a40e44f0d47884e5899707cf32ba800b8a0eb\",\"hash\":\"sha256:4f385509d9af5f13a8c59aeac7e58c3f0d5751a785390d1e9fef7ed93f86a719\"}\n{\"seq\":2,\"ts\":\"2026-10-02T21:23:59.525Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":37,\"rejected\":[]},\"prevHash\":\"sha256:4f385509d9af5f13a8c59aeac7e58c3f0d5751a785390d1e9fef7ed93f86a719\",\"hash\":\"sha256:c2e742699954aaf1f7f45201b9952fb588ac3dc9e7a8df39b80bf0dffd044822\"}\n{\"seq\":3,\"ts\":\"2026-10-02T21:23:59.525Z\",\"type\":\"USER_INPUT\",\"actor\":{\"kind\":\"user\"},\"data\":{\"fields\":[\"BT-84\"]},\"prevHash\":\"sha256:c2e742699954aaf1f7f45201b9952fb588ac3dc9e7a8df39b80bf0dffd044822\",\"hash\":\"sha256:2bfae8b4200cee06aedbe59ed8a8a0b61fb8275a4200dab0a7fef91dcd971705\"}\n{\"seq\":4,\"ts\":\"2026-10-02T21:23:59.525Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":1,\"vatGroups\":1},\"prevHash\":\"sha256:2bfae8b4200cee06aedbe59ed8a8a0b61fb8275a4200dab0a7fef91dcd971705\",\"hash\":\"sha256:1ba86339bb9dae7c0d4a2a33a4a27bf9d3ca822051b64e19eca8905394e7f9e2\"}\n{\"seq\":5,\"ts\":\"2026-10-02T21:23:59.525Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:1ba86339bb9dae7c0d4a2a33a4a27bf9d3ca822051b64e19eca8905394e7f9e2\",\"hash\":\"sha256:db446ed13e6e5dbaff14bcca2719e352d5beefaac1decab7009e84d638c3e55a\"}\n{\"seq\":6,\"ts\":\"2026-10-02T21:23:59.525Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270\",\"data\":{},\"prevHash\":\"sha256:db446ed13e6e5dbaff14bcca2719e352d5beefaac1decab7009e84d638c3e55a\",\"hash\":\"sha256:76c4012de835320e9d8dd65a340cff9563552f8858de383ef930ccbc56de4eba\"}\n{\"seq\":7,\"ts\":\"2026-10-02T21:23:59.553Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"68890637522a6d3ec7e922a397c0dcaeafad86e18a45ae102bb593f759010983\"},\"prevHash\":\"sha256:76c4012de835320e9d8dd65a340cff9563552f8858de383ef930ccbc56de4eba\",\"hash\":\"sha256:fc878eef9e874dafd2240f1fb209252512fc599d6d8c13dc41219acd99d161bb\"}\n{\"seq\":8,\"ts\":\"2026-10-02T21:23:59.553Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:e9b1bb1896910e7f60a6bf5e036d439d79b076645514465305b12ee8a0743270\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:fc878eef9e874dafd2240f1fb209252512fc599d6d8c13dc41219acd99d161bb\",\"hash\":\"sha256:73583c0cb545563ff17448281201c4d056cceb1ccc8b6bbadf42186ef1b5fb42\"}\n","iterations":0,"costUsd":"0.000000","extraction":{"header":{"invoiceNumber":{"value":"R123456789","quote":"Rechnungsnummer R123456789","page":1},"issueDate":{"value":"18.01.2016","quote":"Rechnungsdatum 18.01.2016","page":1},"dueDate":{"value":"01.02.2016","quote":"Fälligkeitsdatum 01.02.2016","page":1},"buyerReference":{"value":"04011000-12349-88","quote":"Leitweg-ID / Käuferreferenz 04011000-12349-88","page":1},"orderReference":{"value":"","quote":"","page":1},"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Die Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo\n","quote":"Zahlungsbedingungen\nDie Zahlung wird als Teilleistung monatl. Erbracht und ist monatl. zum letzten Werktag fällig. Jahresabo","page":1}},"seller":{"name":{"value":"Brückner & Söhne Haustechnik KG","quote":"Brückner & Söhne Haustechnik KG","page":1},"street":{"value":"Postfach 123456","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Musterstadt","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"postcode":{"value":"12345","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"country":{"value":"DE","quote":"Brückner & Söhne Haustechnik KG · Postfach 123456 · 12345 Musterstadt · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":{"value":"","quote":"","page":1},"legalRegistrationId":{"value":"HRB 123","quote":"Handelsregister: HRB 123","page":1},"vatId":{"value":"DE123456789","quote":"USt-IdNr.: DE123456789","page":1},"taxNumber":{"value":"","quote":"","page":1},"contactName":{"value":"Auskunft erteilt Ihnen","quote":"Ansprechpartner: Auskunft erteilt Ihnen","page":1},"contactPhone":{"value":"1234567890","quote":"Telefon: 1234567890","page":1},"contactEmail":{"value":"seller@test.de","quote":"E-Mail: seller@test.de","page":1}},"buyer":{"name":{"value":"Landesamt für Statistik Musterland","quote":"Landesamt für Statistik Musterland","page":1},"street":{"value":"Lindenallee 47","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Tannenberg","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"postcode":{"value":"12345","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"country":{"value":"DE","quote":"Landesamt für Statistik Musterland\nLindenallee 47\n12345 Tannenberg\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"","quote":"","page":1},"accountName":{"value":"","quote":"","page":1}},"vatExemptions":[],"lines":[{"rowQuote":"0020 Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung\n12 Stk. (XPP) 197,89 € 19 % (S) 2.374,68 €","page":1,"lineId":"0020","name":"Zentrale Administrationslösung\nBereitstellung von Werkzeugen\nzur Administration/Betreuung","quantity":"12","unit":"Stk. (XPP)","netPrice":"197,89","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"2.374,68"}],"documentTotals":{"lineTotal":{"value":"2.374,68 €","quote":"Summe netto 2.374,68 €","page":1},"taxTotal":{"value":"451,19 €","quote":"USt 19 % (S) auf 2.374,68 € 451,19 €","page":1},"grandTotal":{"value":"2.825,87 €","quote":"Rechnungsbetrag 2.825,87 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/missing-iban.pdf b/apps/web/public/gallery/missing-iban.pdf new file mode 100644 index 0000000..08cf3e3 Binary files /dev/null and b/apps/web/public/gallery/missing-iban.pdf differ diff --git a/apps/web/public/gallery/missing-leitweg.json b/apps/web/public/gallery/missing-leitweg.json new file mode 100644 index 0000000..a1b75ee --- /dev/null +++ b/apps/web/public/gallery/missing-leitweg.json @@ -0,0 +1 @@ +{"id":"missing-leitweg","title":"Leitweg-ID missing","story":"The buyer reference is not printed. Verdict asks for it instead of inventing one.","pdf":"missing-leitweg.pdf","synthetic":true,"run":[{"at":17,"event":{"type":"start","pages":["Nordwind Fachverlag GmbH\nNordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE\nStadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer 123456XX\nRechnungsdatum 04.04.2016\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €\n2 Porto + Versandkosten 1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €\nSumme netto 314,86 €\nUSt 7 % (S) auf 314,86 € 22,04 €\nUmsatzsteuer 22,04 €\nRechnungsbetrag 336,90 €\nZahlungsbedingungen\nZahlbar sofort ohne Abzug.\nNordwind Fachverlag GmbH\nLindenallee 47\n12345 Tannenberg\nDE\nAnsprechpartner: Paul Neumann\nTelefon: +49 1234-5678\nE-Mail: seller@email.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nUSt-IdNr.: DE 123456789\nHandelsregister: HRA 1172 Amtsgericht Eichstedt\nSeite 1 von 1"],"uploadHash":"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8"}},{"at":17,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:55:51.536Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:d46ddbc3a3d58939f46b776c759adf532ad41ce2695d4b20cdf095ac248a1b35"}}},{"at":5718,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:55:57.237Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:9f72de378971eb158d04135f6e862a8cc41a98b5cc81555e6fd6dad31e1ad730","promptTokens":1229,"completionTokens":1431,"costUsd":"0.00165660","latencyMs":5700},"prevHash":"sha256:d46ddbc3a3d58939f46b776c759adf532ad41ce2695d4b20cdf095ac248a1b35","hash":"sha256:410e08f461abd331439c8c1c2eafda294b7578e9b7d4a366ef4fdc641b25d77e"}}},{"at":5719,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:55:57.238Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":45,"rejected":[]},"prevHash":"sha256:410e08f461abd331439c8c1c2eafda294b7578e9b7d4a366ef4fdc641b25d77e","hash":"sha256:e24730db21c1cbc6600316d40f9bca654fc97d08459873d908adf728ba3d1998"}}},{"at":6577,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:55:58.096Z","type":"REPAIR_PLAN","actor":{"kind":"model","model":"nvidia/Nemotron-3-Ultra-550b-a55b"},"data":{"promptHash":"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad","promptTokens":1199,"completionTokens":102,"costUsd":"0.00150500","latencyMs":857},"prevHash":"sha256:e24730db21c1cbc6600316d40f9bca654fc97d08459873d908adf728ba3d1998","hash":"sha256:96199b6ee12ebc7c4363738b0136c580e5fc7935fa13f58359a992bd8f2acdcd"}}},{"at":6578,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:55:58.097Z","type":"PATCH_GUARD","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":[],"rejected":[],"unresolvable":["BT-10 is required but not found on the document (at /buyerReference)"]},"prevHash":"sha256:96199b6ee12ebc7c4363738b0136c580e5fc7935fa13f58359a992bd8f2acdcd","hash":"sha256:3cb45d408436aea14bb2405a4a178cca0af4ad4cfb0ff939843eff4ddf043b73"}}},{"at":6578,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:55:58.097Z","type":"NEEDS_INPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"missing":["BT-10"],"openIssues":["MISSING"]},"prevHash":"sha256:3cb45d408436aea14bb2405a4a178cca0af4ad4cfb0ff939843eff4ddf043b73","hash":"sha256:b20065d517aaeff52ab0fe601cee4c4b0aa3f44a8bcddf9bb76fff32f27e5621"}}},{"at":6578,"event":{"type":"result","result":{"state":"NEEDS_INPUT","invoice":{"number":"123456XX","issueDate":"2016-04-04","typeCode":"380","currency":"EUR","paymentTerms":"Zahlbar sofort ohne Abzug.","seller":{"name":"Nordwind Fachverlag GmbH","legalRegistrationId":"HRA 1172 Amtsgericht Eichstedt","vatId":"DE 123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Lindenallee 47","city":"Tannenberg","postcode":"12345","countryCode":"DE"},"contact":{"name":"Paul Neumann","phone":"+49 1234-5678","email":"seller@email.de"}},"buyer":{"name":"Stadtverwaltung Birkenfeld","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Am Markt 3","city":"Eichstedt","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"1","name":"Zeitschrift Seminar: Vergaberecht kompakt","quantity":"1","unitCode":"XPP","netPrice":"288.79","vatCategory":"S","vatRate":"7"},{"id":"2","name":"Porto + Versandkosten","quantity":"1","unitCode":"XPP","netPrice":"26.07","vatCategory":"S","vatRate":"7"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer 123456XX"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 04.04.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlbar sofort ohne Abzug."}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE 123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Paul Neumann"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: +49 1234-5678"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: seller@email.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 314,86 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 7 % (S) auf 314,86 € 22,04 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 336,90 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}]],"missing":[{"path":"buyerReference","bt":"BT-10","reason":"not found on the document"}],"openIssues":[{"id":"MISSING","message":"BT-10 is required but not found on the document","location":"/buyerReference"}],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":null,"amounts":null,"documentTotals":{"lineTotal":"314.86","taxTotal":"22.04","grandTotal":"336.90","lines":["288.79","26.07"]},"xml":null,"auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:55:51.536Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:d46ddbc3a3d58939f46b776c759adf532ad41ce2695d4b20cdf095ac248a1b35\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:55:57.237Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:9f72de378971eb158d04135f6e862a8cc41a98b5cc81555e6fd6dad31e1ad730\",\"promptTokens\":1229,\"completionTokens\":1431,\"costUsd\":\"0.00165660\",\"latencyMs\":5700},\"prevHash\":\"sha256:d46ddbc3a3d58939f46b776c759adf532ad41ce2695d4b20cdf095ac248a1b35\",\"hash\":\"sha256:410e08f461abd331439c8c1c2eafda294b7578e9b7d4a366ef4fdc641b25d77e\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:55:57.238Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":45,\"rejected\":[]},\"prevHash\":\"sha256:410e08f461abd331439c8c1c2eafda294b7578e9b7d4a366ef4fdc641b25d77e\",\"hash\":\"sha256:e24730db21c1cbc6600316d40f9bca654fc97d08459873d908adf728ba3d1998\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:55:58.096Z\",\"type\":\"REPAIR_PLAN\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/Nemotron-3-Ultra-550b-a55b\"},\"data\":{\"promptHash\":\"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad\",\"promptTokens\":1199,\"completionTokens\":102,\"costUsd\":\"0.00150500\",\"latencyMs\":857},\"prevHash\":\"sha256:e24730db21c1cbc6600316d40f9bca654fc97d08459873d908adf728ba3d1998\",\"hash\":\"sha256:96199b6ee12ebc7c4363738b0136c580e5fc7935fa13f58359a992bd8f2acdcd\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:55:58.097Z\",\"type\":\"PATCH_GUARD\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":[],\"rejected\":[],\"unresolvable\":[\"BT-10 is required but not found on the document (at /buyerReference)\"]},\"prevHash\":\"sha256:96199b6ee12ebc7c4363738b0136c580e5fc7935fa13f58359a992bd8f2acdcd\",\"hash\":\"sha256:3cb45d408436aea14bb2405a4a178cca0af4ad4cfb0ff939843eff4ddf043b73\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:55:58.097Z\",\"type\":\"NEEDS_INPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"missing\":[\"BT-10\"],\"openIssues\":[\"MISSING\"]},\"prevHash\":\"sha256:3cb45d408436aea14bb2405a4a178cca0af4ad4cfb0ff939843eff4ddf043b73\",\"hash\":\"sha256:b20065d517aaeff52ab0fe601cee4c4b0aa3f44a8bcddf9bb76fff32f27e5621\"}\n","iterations":1,"costUsd":"0.003162","extraction":{"header":{"invoiceNumber":{"value":"123456XX","quote":"Rechnungsnummer 123456XX","page":1},"issueDate":{"value":"04.04.2016","quote":"Rechnungsdatum 04.04.2016","page":1},"dueDate":null,"buyerReference":null,"orderReference":null,"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Zahlbar sofort ohne Abzug.","quote":"Zahlbar sofort ohne Abzug.","page":1}},"seller":{"name":{"value":"Nordwind Fachverlag GmbH","quote":"Nordwind Fachverlag GmbH","page":1},"street":{"value":"Lindenallee 47","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"addressLine2":null,"city":{"value":"Tannenberg","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"postcode":{"value":"12345","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"country":{"value":"DE","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":null,"legalRegistrationId":{"value":"HRA 1172 Amtsgericht Eichstedt","quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt","page":1},"vatId":{"value":"DE 123456789","quote":"USt-IdNr.: DE 123456789","page":1},"taxNumber":null,"contactName":{"value":"Paul Neumann","quote":"Ansprechpartner: Paul Neumann","page":1},"contactPhone":{"value":"+49 1234-5678","quote":"Telefon: +49 1234-5678","page":1},"contactEmail":{"value":"seller@email.de","quote":"E-Mail: seller@email.de","page":1}},"buyer":{"name":{"value":"Stadtverwaltung Birkenfeld","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"street":{"value":"Am Markt 3","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"addressLine2":null,"city":{"value":"Eichstedt","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"postcode":{"value":"12345","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"country":{"value":"DE","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":null},"vatExemptions":[],"lines":[{"rowQuote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €","page":1,"lineId":"1","name":"Zeitschrift Seminar:\nVergaberecht kompakt","quantity":"1","unit":"Stk. (XPP)","netPrice":"288,79 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"288,79 €"},{"rowQuote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €","page":1,"lineId":"2","name":"Porto + Versandkosten","quantity":"1","unit":"Stk. (XPP)","netPrice":"26,07 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"26,07 €"}],"documentTotals":{"lineTotal":{"value":"314,86 €","quote":"Summe netto 314,86 €","page":1},"taxTotal":{"value":"22,04 €","quote":"USt 7 % (S) auf 314,86 € 22,04 €","page":1},"grandTotal":{"value":"336,90 €","quote":"Rechnungsbetrag 336,90 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}],"answer":[{"path":"buyerReference","value":"04011000-12345-03"}],"followUp":[{"at":16,"event":{"type":"start","pages":["Nordwind Fachverlag GmbH\nNordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE\nStadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer 123456XX\nRechnungsdatum 04.04.2016\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €\n2 Porto + Versandkosten 1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €\nSumme netto 314,86 €\nUSt 7 % (S) auf 314,86 € 22,04 €\nUmsatzsteuer 22,04 €\nRechnungsbetrag 336,90 €\nZahlungsbedingungen\nZahlbar sofort ohne Abzug.\nNordwind Fachverlag GmbH\nLindenallee 47\n12345 Tannenberg\nDE\nAnsprechpartner: Paul Neumann\nTelefon: +49 1234-5678\nE-Mail: seller@email.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nUSt-IdNr.: DE 123456789\nHandelsregister: HRA 1172 Amtsgericht Eichstedt\nSeite 1 von 1"],"uploadHash":"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8"}},{"at":16,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:55:58.113Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:77be3d6d3fabe5b3147c3b726cc5ba32cc991c32ccc9d6306b23e4ae6701ab06"}}},{"at":16,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:55:58.113Z","type":"EXTRACT","actor":{"kind":"core","version":"0.1.0"},"data":{"reused":true},"prevHash":"sha256:77be3d6d3fabe5b3147c3b726cc5ba32cc991c32ccc9d6306b23e4ae6701ab06","hash":"sha256:c10e0f4e3956c03b9a62f21c4f3dff3a7ab3ce8b1204615b96f7cb786504dfb6"}}},{"at":18,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:55:58.115Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":45,"rejected":[]},"prevHash":"sha256:c10e0f4e3956c03b9a62f21c4f3dff3a7ab3ce8b1204615b96f7cb786504dfb6","hash":"sha256:a901d9d399e548df156775da9b2931d97d1c9f43d7e3bd337b0ebce3d8b137a5"}}},{"at":18,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:55:58.115Z","type":"USER_INPUT","actor":{"kind":"user"},"data":{"fields":["BT-10"]},"prevHash":"sha256:a901d9d399e548df156775da9b2931d97d1c9f43d7e3bd337b0ebce3d8b137a5","hash":"sha256:499f355e23e13c5e94dba60d8f95df33af9f2926e17ff036c1d227449375e7a5"}}},{"at":19,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:55:58.115Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":2,"vatGroups":1},"prevHash":"sha256:499f355e23e13c5e94dba60d8f95df33af9f2926e17ff036c1d227449375e7a5","hash":"sha256:4d024404e2518638b11ff851f955dac1f8ced116186c860bde1eea89aadd7e74"}}},{"at":19,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:55:58.116Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:4d024404e2518638b11ff851f955dac1f8ced116186c860bde1eea89aadd7e74","hash":"sha256:c112736bfd8f41367ad3dc63fb146dd17506e661f24085d082ad9bed39aaff64"}}},{"at":19,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T20:55:58.116Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24","data":{},"prevHash":"sha256:c112736bfd8f41367ad3dc63fb146dd17506e661f24085d082ad9bed39aaff64","hash":"sha256:ddca211daae0956743754ea76569108bb8d0dde5d388e4242d38f5e610835759"}}},{"at":50,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T20:55:58.147Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24","data":{"valid":true,"errorCount":0,"reportHash":"3d3891bcf06f5f686b794b7ee8326c0267ab1a727dc556c54643ef213a207574"},"prevHash":"sha256:ddca211daae0956743754ea76569108bb8d0dde5d388e4242d38f5e610835759","hash":"sha256:73b6f70abb5c610790473a6e95385c165c00d14af487814671b7d03e7830b939"}}},{"at":51,"event":{"type":"audit","event":{"seq":8,"ts":"2026-10-02T20:55:58.147Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24"},"fieldsWithoutProvenance":0},"prevHash":"sha256:73b6f70abb5c610790473a6e95385c165c00d14af487814671b7d03e7830b939","hash":"sha256:9ac3bd820aa662f4fbcb58dc802a8fe450c1379af70ad3c178ff2e01d2fac839"}}},{"at":51,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"123456XX","issueDate":"2016-04-04","typeCode":"380","currency":"EUR","buyerReference":"04011000-12345-03","paymentTerms":"Zahlbar sofort ohne Abzug.","seller":{"name":"Nordwind Fachverlag GmbH","legalRegistrationId":"HRA 1172 Amtsgericht Eichstedt","vatId":"DE 123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Lindenallee 47","city":"Tannenberg","postcode":"12345","countryCode":"DE"},"contact":{"name":"Paul Neumann","phone":"+49 1234-5678","email":"seller@email.de"}},"buyer":{"name":"Stadtverwaltung Birkenfeld","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Am Markt 3","city":"Eichstedt","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"1","name":"Zeitschrift Seminar: Vergaberecht kompakt","quantity":"1","unitCode":"XPP","netPrice":"288.79","vatCategory":"S","vatRate":"7"},{"id":"2","name":"Porto + Versandkosten","quantity":"1","unitCode":"XPP","netPrice":"26.07","vatCategory":"S","vatRate":"7"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer 123456XX"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 04.04.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlbar sofort ohne Abzug."}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE 123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Paul Neumann"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: +49 1234-5678"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: seller@email.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 314,86 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 7 % (S) auf 314,86 € 22,04 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 336,90 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €"}],["buyerReference",{"kind":"user","enteredAt":"2026-10-02T20:55:58.115Z"}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"3d3891bcf06f5f686b794b7ee8326c0267ab1a727dc556c54643ef213a207574","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"314.86","taxTotal":"22.04","grandTotal":"336.90"},"documentTotals":{"lineTotal":"314.86","taxTotal":"22.04","grandTotal":"336.90","lines":["288.79","26.07"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n 123456XX\n 380\n \n 20160404\n \n \n \n \n \n 1\n \n \n Zeitschrift Seminar: Vergaberecht kompakt\n \n \n \n 288.79\n \n \n \n 1\n \n \n \n VAT\n S\n 7\n \n \n 288.79\n \n \n \n \n \n 2\n \n \n Porto + Versandkosten\n \n \n \n 26.07\n \n \n \n 1\n \n \n \n VAT\n S\n 7\n \n \n 26.07\n \n \n \n \n 04011000-12345-03\n \n Nordwind Fachverlag GmbH\n \n HRA 1172 Amtsgericht Eichstedt\n \n \n Paul Neumann\n \n +49 1234-5678\n \n \n seller@email.de\n \n \n \n 12345\n Lindenallee 47\n Tannenberg\n DE\n \n \n seller@email.de\n \n \n DE 123456789\n \n \n \n Stadtverwaltung Birkenfeld\n \n 12345\n Am Markt 3\n Eichstedt\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n \n \n \n 22.04\n VAT\n 314.86\n S\n 7\n \n \n Zahlbar sofort ohne Abzug.\n \n \n 314.86\n 314.86\n 22.04\n 336.90\n 336.90\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:55:58.113Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:d68ca47f03b6f4097b97da275db447aba582358c149ab2899d0af493b2d2bcb8\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:77be3d6d3fabe5b3147c3b726cc5ba32cc991c32ccc9d6306b23e4ae6701ab06\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:55:58.113Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"reused\":true},\"prevHash\":\"sha256:77be3d6d3fabe5b3147c3b726cc5ba32cc991c32ccc9d6306b23e4ae6701ab06\",\"hash\":\"sha256:c10e0f4e3956c03b9a62f21c4f3dff3a7ab3ce8b1204615b96f7cb786504dfb6\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:55:58.115Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":45,\"rejected\":[]},\"prevHash\":\"sha256:c10e0f4e3956c03b9a62f21c4f3dff3a7ab3ce8b1204615b96f7cb786504dfb6\",\"hash\":\"sha256:a901d9d399e548df156775da9b2931d97d1c9f43d7e3bd337b0ebce3d8b137a5\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:55:58.115Z\",\"type\":\"USER_INPUT\",\"actor\":{\"kind\":\"user\"},\"data\":{\"fields\":[\"BT-10\"]},\"prevHash\":\"sha256:a901d9d399e548df156775da9b2931d97d1c9f43d7e3bd337b0ebce3d8b137a5\",\"hash\":\"sha256:499f355e23e13c5e94dba60d8f95df33af9f2926e17ff036c1d227449375e7a5\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:55:58.115Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":2,\"vatGroups\":1},\"prevHash\":\"sha256:499f355e23e13c5e94dba60d8f95df33af9f2926e17ff036c1d227449375e7a5\",\"hash\":\"sha256:4d024404e2518638b11ff851f955dac1f8ced116186c860bde1eea89aadd7e74\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:55:58.116Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:4d024404e2518638b11ff851f955dac1f8ced116186c860bde1eea89aadd7e74\",\"hash\":\"sha256:c112736bfd8f41367ad3dc63fb146dd17506e661f24085d082ad9bed39aaff64\"}\n{\"seq\":6,\"ts\":\"2026-10-02T20:55:58.116Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\",\"data\":{},\"prevHash\":\"sha256:c112736bfd8f41367ad3dc63fb146dd17506e661f24085d082ad9bed39aaff64\",\"hash\":\"sha256:ddca211daae0956743754ea76569108bb8d0dde5d388e4242d38f5e610835759\"}\n{\"seq\":7,\"ts\":\"2026-10-02T20:55:58.147Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"3d3891bcf06f5f686b794b7ee8326c0267ab1a727dc556c54643ef213a207574\"},\"prevHash\":\"sha256:ddca211daae0956743754ea76569108bb8d0dde5d388e4242d38f5e610835759\",\"hash\":\"sha256:73b6f70abb5c610790473a6e95385c165c00d14af487814671b7d03e7830b939\"}\n{\"seq\":8,\"ts\":\"2026-10-02T20:55:58.147Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:706bbed65557bda8ad3e07ba640c3ab3b0769ade1c514186e271c7677b2f9e24\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:73b6f70abb5c610790473a6e95385c165c00d14af487814671b7d03e7830b939\",\"hash\":\"sha256:9ac3bd820aa662f4fbcb58dc802a8fe450c1379af70ad3c178ff2e01d2fac839\"}\n","iterations":0,"costUsd":"0.000000","extraction":{"header":{"invoiceNumber":{"value":"123456XX","quote":"Rechnungsnummer 123456XX","page":1},"issueDate":{"value":"04.04.2016","quote":"Rechnungsdatum 04.04.2016","page":1},"dueDate":null,"buyerReference":null,"orderReference":null,"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Zahlbar sofort ohne Abzug.","quote":"Zahlbar sofort ohne Abzug.","page":1}},"seller":{"name":{"value":"Nordwind Fachverlag GmbH","quote":"Nordwind Fachverlag GmbH","page":1},"street":{"value":"Lindenallee 47","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"addressLine2":null,"city":{"value":"Tannenberg","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"postcode":{"value":"12345","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"country":{"value":"DE","quote":"Nordwind Fachverlag GmbH · Lindenallee 47 · 12345 Tannenberg · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":null,"legalRegistrationId":{"value":"HRA 1172 Amtsgericht Eichstedt","quote":"Handelsregister: HRA 1172 Amtsgericht Eichstedt","page":1},"vatId":{"value":"DE 123456789","quote":"USt-IdNr.: DE 123456789","page":1},"taxNumber":null,"contactName":{"value":"Paul Neumann","quote":"Ansprechpartner: Paul Neumann","page":1},"contactPhone":{"value":"+49 1234-5678","quote":"Telefon: +49 1234-5678","page":1},"contactEmail":{"value":"seller@email.de","quote":"E-Mail: seller@email.de","page":1}},"buyer":{"name":{"value":"Stadtverwaltung Birkenfeld","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"street":{"value":"Am Markt 3","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"addressLine2":null,"city":{"value":"Eichstedt","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"postcode":{"value":"12345","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"country":{"value":"DE","quote":"Stadtverwaltung Birkenfeld\nAm Markt 3\n12345 Eichstedt\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":null},"vatExemptions":[],"lines":[{"rowQuote":"1 Zeitschrift Seminar:\nVergaberecht kompakt\n1 Stk. (XPP) 288,79 € 7 % (S) 288,79 €","page":1,"lineId":"1","name":"Zeitschrift Seminar:\nVergaberecht kompakt","quantity":"1","unit":"Stk. (XPP)","netPrice":"288,79 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"288,79 €"},{"rowQuote":"2 Porto + Versandkosten\n1 Stk. (XPP) 26,07 € 7 % (S) 26,07 €","page":1,"lineId":"2","name":"Porto + Versandkosten","quantity":"1","unit":"Stk. (XPP)","netPrice":"26,07 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"7 %","lineAmount":"26,07 €"}],"documentTotals":{"lineTotal":{"value":"314,86 €","quote":"Summe netto 314,86 €","page":1},"taxTotal":{"value":"22,04 €","quote":"USt 7 % (S) auf 314,86 € 22,04 €","page":1},"grandTotal":{"value":"336,90 €","quote":"Rechnungsbetrag 336,90 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/missing-leitweg.pdf b/apps/web/public/gallery/missing-leitweg.pdf new file mode 100644 index 0000000..eddccad Binary files /dev/null and b/apps/web/public/gallery/missing-leitweg.pdf differ diff --git a/apps/web/public/gallery/noisy-layout.json b/apps/web/public/gallery/noisy-layout.json new file mode 100644 index 0000000..8c8fafb --- /dev/null +++ b/apps/web/public/gallery/noisy-layout.json @@ -0,0 +1 @@ +{"id":"noisy-layout","title":"Unusual layout","story":"Plain number format, ISO dates and shuffled blocks still end in a valid e-invoice.","pdf":"noisy-layout.pdf","synthetic":true,"run":[{"at":233,"event":{"type":"start","pages":["RECHNUNG\nHagedorn Laborbedarf GmbH\nRechnung Nr. RE-2016-1011\nDatum 2016-03-02\nZahlbar bis 2016-03-16\nKäuferreferenz (Leitweg-ID) 04011000-12353-76\nRechnungswährung EUR\nEmpfänger\nHochschule Tannenberg\nHafenstraße 12\n12345 Lerchenau\nDE\nE-Rechnungsadresse: buyer@info.de (Schema: EM)\nAbsender\nHagedorn Laborbedarf GmbH\nAm Markt 3\n12345 Eichstedt\nDE\nKontakt: Auskunft erteilt Ihnen\nTel.: 1234567890\nMail: seller@test.de\nE-Rechnungsadresse: seller@email.de (Schema: EM)\nNr. Leistung Anzahl / ME Preis je Einheit MwSt. Gesamt\n0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR\n0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR\n0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR\n0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR\n0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR\nNettobetrag 256.61 EUR\nMwSt. 19 % (S) auf 256.61 EUR 48.76 EUR\nzzgl. MwSt. 48.76 EUR\nGesamtbetrag (brutto) 305.37 EUR\nZahlung an\nIBAN: DE79000000001234567890\nUmsatzsteuer-ID: DE123456789 Registereintrag: HRB 123\nSeite 1 von 1"],"uploadHash":"sha256:85220cc0defdc26b18b1d17e5d17384f1427f47263cb45dd381a54aaf80ea160"}},{"at":234,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:56:16.640Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:85220cc0defdc26b18b1d17e5d17384f1427f47263cb45dd381a54aaf80ea160","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:524b12d37c0e34b75f7185583ce3c4fe2f49b147c948ea451d675fbb21b54408"}}},{"at":6503,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:56:22.909Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:add499f711e724a83cbf5e39b48ccdd8b39ef5f2d8ff46ed6af4723e5293e91e","promptTokens":1370,"completionTokens":1783,"costUsd":"0.00201570","latencyMs":6263},"prevHash":"sha256:524b12d37c0e34b75f7185583ce3c4fe2f49b147c948ea451d675fbb21b54408","hash":"sha256:266533744edbfc3cf29c5868ab342794ba581de5cec15a2363f98bc0228b3e0f"}}},{"at":6514,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:56:22.920Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":68,"rejected":[]},"prevHash":"sha256:266533744edbfc3cf29c5868ab342794ba581de5cec15a2363f98bc0228b3e0f","hash":"sha256:69773956d2c1637462d9474f96469cc0489ed9b82e013f3dc51a7088561c0c14"}}},{"at":7420,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:56:23.827Z","type":"REPAIR_PLAN","actor":{"kind":"model","model":"nvidia/Nemotron-3-Ultra-550b-a55b"},"data":{"promptHash":"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad","promptTokens":1570,"completionTokens":145,"costUsd":"0.00200500","latencyMs":904},"prevHash":"sha256:69773956d2c1637462d9474f96469cc0489ed9b82e013f3dc51a7088561c0c14","hash":"sha256:0364bc9c855ae6bf29b65c6529f9d6d7c5f1e8491526074cdba0c1d713ba2deb"}}},{"at":7422,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:56:23.829Z","type":"PATCH_GUARD","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":["/seller/contact/name","/seller/contact/phone"],"rejected":[],"unresolvable":[]},"prevHash":"sha256:0364bc9c855ae6bf29b65c6529f9d6d7c5f1e8491526074cdba0c1d713ba2deb","hash":"sha256:3a03e8f5bf77ff56d7d5cdd2bb43f2e0495458d0b5861cb03a205d7055ccc449"}}},{"at":7423,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:56:23.830Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":5,"vatGroups":1},"prevHash":"sha256:3a03e8f5bf77ff56d7d5cdd2bb43f2e0495458d0b5861cb03a205d7055ccc449","hash":"sha256:794d4af72322dbc335246282fbff1d20255c56f1a519f81e35a238043c925e60"}}},{"at":7424,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T20:56:23.830Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:794d4af72322dbc335246282fbff1d20255c56f1a519f81e35a238043c925e60","hash":"sha256:b9ebb12c2791a0875ac196569bfb20153a94777d02f8ff35d552508136ccab32"}}},{"at":7425,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T20:56:23.831Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4","data":{},"prevHash":"sha256:b9ebb12c2791a0875ac196569bfb20153a94777d02f8ff35d552508136ccab32","hash":"sha256:77779662a3d8079df30e22e291956a011cefdda4a5f8a951cefdf6cb829490e3"}}},{"at":7459,"event":{"type":"audit","event":{"seq":8,"ts":"2026-10-02T20:56:23.865Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4","data":{"valid":true,"errorCount":0,"reportHash":"311ef00aacd6d8b14e0bbc3cdec949b3d63f3828a919357f104bde0ac89f3a40"},"prevHash":"sha256:77779662a3d8079df30e22e291956a011cefdda4a5f8a951cefdf6cb829490e3","hash":"sha256:547e0b4d74ac60861b4a9eea8fce74cd8e918c8796dda4052fcc240c8489fd40"}}},{"at":7459,"event":{"type":"audit","event":{"seq":9,"ts":"2026-10-02T20:56:23.866Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4"},"fieldsWithoutProvenance":0},"prevHash":"sha256:547e0b4d74ac60861b4a9eea8fce74cd8e918c8796dda4052fcc240c8489fd40","hash":"sha256:6a781450755ff113c3399e7d7beedc4a2325f232d19208fd20f35171a8017514"}}},{"at":7460,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"RE-2016-1011","issueDate":"2016-03-02","typeCode":"380","currency":"EUR","dueDate":"2016-03-16","buyerReference":"04011000-12353-76","seller":{"name":"Hagedorn Laborbedarf GmbH","legalRegistrationId":"HRB 123","vatId":"DE123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Am Markt 3","city":"Eichstedt","postcode":"12345","countryCode":"DE"},"contact":{"name":"Auskunft erteilt Ihnen","phone":"1234567890","email":"seller@test.de"}},"buyer":{"name":"Hochschule Tannenberg","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Hafenstraße 12","city":"Lerchenau","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"0010","name":"Keyboard/Maus - weiß","quantity":"1","unitCode":"XPP","netPrice":"12.52","vatCategory":"S","vatRate":"19"},{"id":"0020","name":"MX Master","quantity":"2","unitCode":"XPP","netPrice":"63.45","vatCategory":"S","vatRate":"19"},{"id":"0030","name":"Tastatur Deutsch","quantity":"2","unitCode":"XPP","netPrice":"47.82","vatCategory":"S","vatRate":"19"},{"id":"0040","name":"Beschaffungspauschale","quantity":"1","unitCode":"XPP","netPrice":"11.75","vatCategory":"S","vatRate":"19"},{"id":"0050","name":"Lieferkosten","quantity":"1","unitCode":"XPP","netPrice":"9.8","vatCategory":"S","vatRate":"19"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["lines.2.vatRate",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.vatCategory",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.id",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.name",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.quantity",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.unitCode",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.2.netPrice",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["lines.3.vatRate",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.vatCategory",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.id",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.name",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.quantity",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.unitCode",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.3.netPrice",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["lines.4.vatRate",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.vatCategory",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.id",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.name",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.quantity",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.unitCode",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["lines.4.netPrice",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["seller.name",{"kind":"evidence","page":1,"quote":"Hagedorn Laborbedarf GmbH"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Hochschule Tannenberg"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"E-Rechnungsadresse: seller@email.de (Schema: EM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"E-Rechnungsadresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnung Nr. RE-2016-1011"}],["issueDate",{"kind":"evidence","page":1,"quote":"Datum 2016-03-02"}],["currency",{"kind":"evidence","page":1,"quote":"Rechnungswährung EUR"}],["dueDate",{"kind":"evidence","page":1,"quote":"Zahlbar bis 2016-03-16"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Käuferreferenz (Leitweg-ID) 04011000-12353-76"}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Registereintrag: HRB 123"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"Umsatzsteuer-ID: DE123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Am Markt 3"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"12345 Eichstedt\nDE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"12345 Eichstedt\nDE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"12345 Eichstedt\nDE"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"Mail: seller@test.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Hafenstraße 12"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"12345 Lerchenau\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"12345 Lerchenau\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"12345 Lerchenau\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Nettobetrag 256.61 EUR"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"MwSt. 19 % (S) auf 256.61 EUR 48.76 EUR"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Gesamtbetrag (brutto) 305.37 EUR"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR"}],["documentTotals.lines.2",{"kind":"evidence","page":1,"quote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR"}],["documentTotals.lines.3",{"kind":"evidence","page":1,"quote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR"}],["documentTotals.lines.4",{"kind":"evidence","page":1,"quote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Kontakt: Auskunft erteilt Ihnen"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Tel.: 1234567890"}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[{"op":"add","path":"/seller/contact/name","value":"Auskunft erteilt Ihnen","quote":"Kontakt: Auskunft erteilt Ihnen","page":1,"normalized":"Auskunft erteilt Ihnen"},{"op":"add","path":"/seller/contact/phone","value":"1234567890","quote":"Tel.: 1234567890","page":1,"normalized":"1234567890"}],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"311ef00aacd6d8b14e0bbc3cdec949b3d63f3828a919357f104bde0ac89f3a40","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"256.61","taxTotal":"48.76","grandTotal":"305.37"},"documentTotals":{"lineTotal":"256.61","taxTotal":"48.76","grandTotal":"305.37","lines":["12.52","126.90","95.64","11.75","9.80"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n RE-2016-1011\n 380\n \n 20160302\n \n \n \n \n \n 0010\n \n \n Keyboard/Maus - weiß\n \n \n \n 12.52\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 12.52\n \n \n \n \n \n 0020\n \n \n MX Master\n \n \n \n 63.45\n \n \n \n 2\n \n \n \n VAT\n S\n 19\n \n \n 126.90\n \n \n \n \n \n 0030\n \n \n Tastatur Deutsch\n \n \n \n 47.82\n \n \n \n 2\n \n \n \n VAT\n S\n 19\n \n \n 95.64\n \n \n \n \n \n 0040\n \n \n Beschaffungspauschale\n \n \n \n 11.75\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 11.75\n \n \n \n \n \n 0050\n \n \n Lieferkosten\n \n \n \n 9.8\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 9.80\n \n \n \n \n 04011000-12353-76\n \n Hagedorn Laborbedarf GmbH\n \n HRB 123\n \n \n Auskunft erteilt Ihnen\n \n 1234567890\n \n \n seller@test.de\n \n \n \n 12345\n Am Markt 3\n Eichstedt\n DE\n \n \n seller@email.de\n \n \n DE123456789\n \n \n \n Hochschule Tannenberg\n \n 12345\n Hafenstraße 12\n Lerchenau\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n \n \n \n 48.76\n VAT\n 256.61\n S\n 19\n \n \n \n 20160316\n \n \n \n 256.61\n 256.61\n 48.76\n 305.37\n 305.37\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:56:16.640Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:85220cc0defdc26b18b1d17e5d17384f1427f47263cb45dd381a54aaf80ea160\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:524b12d37c0e34b75f7185583ce3c4fe2f49b147c948ea451d675fbb21b54408\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:56:22.909Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:add499f711e724a83cbf5e39b48ccdd8b39ef5f2d8ff46ed6af4723e5293e91e\",\"promptTokens\":1370,\"completionTokens\":1783,\"costUsd\":\"0.00201570\",\"latencyMs\":6263},\"prevHash\":\"sha256:524b12d37c0e34b75f7185583ce3c4fe2f49b147c948ea451d675fbb21b54408\",\"hash\":\"sha256:266533744edbfc3cf29c5868ab342794ba581de5cec15a2363f98bc0228b3e0f\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:56:22.920Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":68,\"rejected\":[]},\"prevHash\":\"sha256:266533744edbfc3cf29c5868ab342794ba581de5cec15a2363f98bc0228b3e0f\",\"hash\":\"sha256:69773956d2c1637462d9474f96469cc0489ed9b82e013f3dc51a7088561c0c14\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:56:23.827Z\",\"type\":\"REPAIR_PLAN\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/Nemotron-3-Ultra-550b-a55b\"},\"data\":{\"promptHash\":\"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad\",\"promptTokens\":1570,\"completionTokens\":145,\"costUsd\":\"0.00200500\",\"latencyMs\":904},\"prevHash\":\"sha256:69773956d2c1637462d9474f96469cc0489ed9b82e013f3dc51a7088561c0c14\",\"hash\":\"sha256:0364bc9c855ae6bf29b65c6529f9d6d7c5f1e8491526074cdba0c1d713ba2deb\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:56:23.829Z\",\"type\":\"PATCH_GUARD\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":[\"/seller/contact/name\",\"/seller/contact/phone\"],\"rejected\":[],\"unresolvable\":[]},\"prevHash\":\"sha256:0364bc9c855ae6bf29b65c6529f9d6d7c5f1e8491526074cdba0c1d713ba2deb\",\"hash\":\"sha256:3a03e8f5bf77ff56d7d5cdd2bb43f2e0495458d0b5861cb03a205d7055ccc449\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:56:23.830Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":5,\"vatGroups\":1},\"prevHash\":\"sha256:3a03e8f5bf77ff56d7d5cdd2bb43f2e0495458d0b5861cb03a205d7055ccc449\",\"hash\":\"sha256:794d4af72322dbc335246282fbff1d20255c56f1a519f81e35a238043c925e60\"}\n{\"seq\":6,\"ts\":\"2026-10-02T20:56:23.830Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:794d4af72322dbc335246282fbff1d20255c56f1a519f81e35a238043c925e60\",\"hash\":\"sha256:b9ebb12c2791a0875ac196569bfb20153a94777d02f8ff35d552508136ccab32\"}\n{\"seq\":7,\"ts\":\"2026-10-02T20:56:23.831Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4\",\"data\":{},\"prevHash\":\"sha256:b9ebb12c2791a0875ac196569bfb20153a94777d02f8ff35d552508136ccab32\",\"hash\":\"sha256:77779662a3d8079df30e22e291956a011cefdda4a5f8a951cefdf6cb829490e3\"}\n{\"seq\":8,\"ts\":\"2026-10-02T20:56:23.865Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"311ef00aacd6d8b14e0bbc3cdec949b3d63f3828a919357f104bde0ac89f3a40\"},\"prevHash\":\"sha256:77779662a3d8079df30e22e291956a011cefdda4a5f8a951cefdf6cb829490e3\",\"hash\":\"sha256:547e0b4d74ac60861b4a9eea8fce74cd8e918c8796dda4052fcc240c8489fd40\"}\n{\"seq\":9,\"ts\":\"2026-10-02T20:56:23.866Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:9fbb4184cb1870aa240e8f9555e6584cf01107b65076d05014a8c9ebb245c8a4\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:547e0b4d74ac60861b4a9eea8fce74cd8e918c8796dda4052fcc240c8489fd40\",\"hash\":\"sha256:6a781450755ff113c3399e7d7beedc4a2325f232d19208fd20f35171a8017514\"}\n","iterations":1,"costUsd":"0.004021","extraction":{"header":{"invoiceNumber":{"value":"RE-2016-1011","quote":"Rechnung Nr. RE-2016-1011","page":1},"issueDate":{"value":"2016-03-02","quote":"Datum 2016-03-02","page":1},"dueDate":{"value":"2016-03-16","quote":"Zahlbar bis 2016-03-16","page":1},"buyerReference":{"value":"04011000-12353-76","quote":"Käuferreferenz (Leitweg-ID) 04011000-12353-76","page":1},"orderReference":{"value":"","quote":"","page":1},"currency":{"value":"EUR","quote":"Rechnungswährung EUR","page":1},"paymentTerms":{"value":"","quote":"","page":1}},"seller":{"name":{"value":"Hagedorn Laborbedarf GmbH","quote":"Hagedorn Laborbedarf GmbH","page":1},"street":{"value":"Am Markt 3","quote":"Am Markt 3","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Eichstedt","quote":"12345 Eichstedt\nDE","page":1},"postcode":{"value":"12345","quote":"12345 Eichstedt\nDE","page":1},"country":{"value":"DE","quote":"12345 Eichstedt\nDE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"E-Rechnungsadresse: seller@email.de (Schema: EM)","page":1},"sellerId":{"value":"","quote":"","page":1},"legalRegistrationId":{"value":"HRB 123","quote":"Registereintrag: HRB 123","page":1},"vatId":{"value":"DE123456789","quote":"Umsatzsteuer-ID: DE123456789","page":1},"taxNumber":{"value":"","quote":"","page":1},"contactName":{"value":"","quote":"","page":1},"contactPhone":{"value":"","quote":"","page":1},"contactEmail":{"value":"seller@test.de","quote":"Mail: seller@test.de","page":1}},"buyer":{"name":{"value":"Hochschule Tannenberg","quote":"Hochschule Tannenberg","page":1},"street":{"value":"Hafenstraße 12","quote":"Hafenstraße 12","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Lerchenau","quote":"12345 Lerchenau\nDE","page":1},"postcode":{"value":"12345","quote":"12345 Lerchenau\nDE","page":1},"country":{"value":"DE","quote":"12345 Lerchenau\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"E-Rechnungsadresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":{"value":"","quote":"","page":1}},"vatExemptions":[],"lines":[{"rowQuote":"0010 Keyboard/Maus - weiß 1 Stk. (XPP) 12.52 EUR 19 % (S) 12.52 EUR","page":1,"lineId":"0010","name":"Keyboard/Maus - weiß","quantity":"1","unit":"Stk. (XPP)","netPrice":"12.52","priceBaseQuantity":"","vatCategory":"(S)","vatRate":"19 %","lineAmount":"12.52"},{"rowQuote":"0020 MX Master 2 Stk. (XPP) 63.45 EUR 19 % (S) 126.90 EUR","page":1,"lineId":"0020","name":"MX Master","quantity":"2","unit":"Stk. (XPP)","netPrice":"63.45","priceBaseQuantity":"","vatCategory":"(S)","vatRate":"19 %","lineAmount":"126.90"},{"rowQuote":"0030 Tastatur Deutsch 2 Stk. (XPP) 47.82 EUR 19 % (S) 95.64 EUR","page":1,"lineId":"0030","name":"Tastatur Deutsch","quantity":"2","unit":"Stk. (XPP)","netPrice":"47.82","priceBaseQuantity":"","vatCategory":"(S)","vatRate":"19 %","lineAmount":"95.64"},{"rowQuote":"0040 Beschaffungspauschale 1 Stk. (XPP) 11.75 EUR 19 % (S) 11.75 EUR","page":1,"lineId":"0040","name":"Beschaffungspauschale","quantity":"1","unit":"Stk. (XPP)","netPrice":"11.75","priceBaseQuantity":"","vatCategory":"(S)","vatRate":"19 %","lineAmount":"11.75"},{"rowQuote":"0050 Lieferkosten 1 Stk. (XPP) 9.8 EUR 19 % (S) 9.80 EUR","page":1,"lineId":"0050","name":"Lieferkosten","quantity":"1","unit":"Stk. (XPP)","netPrice":"9.8","priceBaseQuantity":"","vatCategory":"(S)","vatRate":"19 %","lineAmount":"9.80"}],"documentTotals":{"lineTotal":{"value":"256.61 EUR","quote":"Nettobetrag 256.61 EUR","page":1},"taxTotal":{"value":"48.76 EUR","quote":"MwSt. 19 % (S) auf 256.61 EUR 48.76 EUR","page":1},"grandTotal":{"value":"305.37 EUR","quote":"Gesamtbetrag (brutto) 305.37 EUR","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/noisy-layout.pdf b/apps/web/public/gallery/noisy-layout.pdf new file mode 100644 index 0000000..5a1740b Binary files /dev/null and b/apps/web/public/gallery/noisy-layout.pdf differ diff --git a/apps/web/public/gallery/seven-lines.json b/apps/web/public/gallery/seven-lines.json new file mode 100644 index 0000000..24bd3d0 --- /dev/null +++ b/apps/web/public/gallery/seven-lines.json @@ -0,0 +1 @@ +{"id":"seven-lines","title":"Seven line items","story":"Seven line items. Nemotron Ultra repairs three fields behind the patch guard; every amount is computed from the lines.","pdf":"seven-lines.pdf","synthetic":true,"run":[{"at":229,"event":{"type":"start","pages":["Kranich Bürosysteme GmbH\nKranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE\nHochschule Tannenberg\nHafenstraße 12\n12345 Lerchenau\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer R123456789\nRechnungsdatum 06.07.2016\nLeitweg-ID / Käuferreferenz 99000000-01944-98\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €\n2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €\n3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €\n4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €\n5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €\n6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €\n7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €\nSumme netto 18.236,72 €\nUSt 19 % (S) auf 18.236,72 € 3.464,98 €\nUmsatzsteuer 3.464,98 €\nRechnungsbetrag 21.701,70 €\nZahlungsbedingungen\nBitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital.\nKranich Bürosysteme GmbH\nAm Markt 3\n12345 Eichstedt\nDE\nAnsprechpartner: Kundenservice -\nVertriebspartner Direktkunden\nTelefon: +49(0)12 3456-7890\nE-Mail: kundenservice@email.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nUSt-IdNr.: DE123456789\nHandelsregister: HRB 123456\nSeite 1 von 1"],"uploadHash":"sha256:bbf34fa5b319d9ec2bed02bc9eae6508b3b4d1e08c14afd7c202a9d85dd88bae"}},{"at":229,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T21:22:26.509Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:bbf34fa5b319d9ec2bed02bc9eae6508b3b4d1e08c14afd7c202a9d85dd88bae","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:e21a7859c0b5e907b30e47939d518d5d6d5e1076a718c9668b2e9484539c3879"}}},{"at":8496,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T21:22:34.776Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:e691686614ceb15c40b605eaaca66a0d818c42ad5c7ef345fe3d0321f0eebd65","promptTokens":1515,"completionTokens":2119,"costUsd":"0.00236160","latencyMs":8261},"prevHash":"sha256:e21a7859c0b5e907b30e47939d518d5d6d5e1076a718c9668b2e9484539c3879","hash":"sha256:b267cf5c04864b1aad79730e0959249bcf7ae7f8959afe2077a28abaed7e7e77"}}},{"at":8508,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T21:22:34.788Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":83,"rejected":[]},"prevHash":"sha256:b267cf5c04864b1aad79730e0959249bcf7ae7f8959afe2077a28abaed7e7e77","hash":"sha256:8efccdfab38d9ad6dca9f8dec76e6b9a39632b1ecef3b7c473b5cbbf41f78d73"}}},{"at":9522,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T21:22:35.802Z","type":"REPAIR_PLAN","actor":{"kind":"model","model":"nvidia/Nemotron-3-Ultra-550b-a55b"},"data":{"promptHash":"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad","promptTokens":1905,"completionTokens":271,"costUsd":"0.00271800","latencyMs":1011},"prevHash":"sha256:8efccdfab38d9ad6dca9f8dec76e6b9a39632b1ecef3b7c473b5cbbf41f78d73","hash":"sha256:8d786b55ba367c907bd7a7ce1e458183433c6ff8f6e6335fde5f357630ec5ac6"}}},{"at":9524,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T21:22:35.804Z","type":"PATCH_GUARD","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":["/seller/contact/name","/seller/contact/phone","/seller/contact/email"],"rejected":[],"unresolvable":[]},"prevHash":"sha256:8d786b55ba367c907bd7a7ce1e458183433c6ff8f6e6335fde5f357630ec5ac6","hash":"sha256:a89c507e899fa6eb547643afcdc2ef2a22de160e77140125f9563157fc7784f4"}}},{"at":9525,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T21:22:35.805Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":7,"vatGroups":1},"prevHash":"sha256:a89c507e899fa6eb547643afcdc2ef2a22de160e77140125f9563157fc7784f4","hash":"sha256:3d5d92f6c9300759751ff1a55d3ee735c548bdf85b56c372c635c337179b1679"}}},{"at":9526,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T21:22:35.806Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:3d5d92f6c9300759751ff1a55d3ee735c548bdf85b56c372c635c337179b1679","hash":"sha256:e34e7d11db2c6fa0e6418e87b255d57ec34ac80a2b2335c3c4f7cfca3c0732b8"}}},{"at":9527,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T21:22:35.807Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241","data":{},"prevHash":"sha256:e34e7d11db2c6fa0e6418e87b255d57ec34ac80a2b2335c3c4f7cfca3c0732b8","hash":"sha256:79d3e6be9df9ff08451c08175ac671e7c9bab2a9862531e71075c3ad707d5843"}}},{"at":9612,"event":{"type":"audit","event":{"seq":8,"ts":"2026-10-02T21:22:35.892Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241","data":{"valid":true,"errorCount":0,"reportHash":"30aec56f4fcdd8637f06f23a28637dd4c1694108a750077ae38621a9d576444b"},"prevHash":"sha256:79d3e6be9df9ff08451c08175ac671e7c9bab2a9862531e71075c3ad707d5843","hash":"sha256:6a5ac1742b3160e0f8d80c130a1a863b796229e9c124bf1789930e8788678cd6"}}},{"at":9613,"event":{"type":"audit","event":{"seq":9,"ts":"2026-10-02T21:22:35.893Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241"},"fieldsWithoutProvenance":0},"prevHash":"sha256:6a5ac1742b3160e0f8d80c130a1a863b796229e9c124bf1789930e8788678cd6","hash":"sha256:6dff9185e01bc2b01996e76d34a45cab74c85390009d273adb0a23a9f29d3d02"}}},{"at":9613,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"R123456789","issueDate":"2016-07-06","typeCode":"380","currency":"EUR","buyerReference":"99000000-01944-98","paymentTerms":"Bitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital.","seller":{"name":"Kranich Bürosysteme GmbH","legalRegistrationId":"HRB 123456","vatId":"DE123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Am Markt 3","city":"Eichstedt","postcode":"12345","countryCode":"DE"},"contact":{"name":"Kundenservice - Vertriebspartner Direktkunden","phone":"+49(0)12 3456-7890","email":"kundenservice@email.de"}},"buyer":{"name":"Hochschule Tannenberg","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Hafenstraße 12","city":"Lerchenau","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"1","name":"Servicegebühren A","quantity":"1","unitCode":"MON","netPrice":"17.24","vatCategory":"S","vatRate":"19"},{"id":"2","name":"Servicegebühren B","quantity":"1","unitCode":"MON","netPrice":"16704.07","vatCategory":"S","vatRate":"19"},{"id":"3","name":"Servicegebühren C","quantity":"1","unitCode":"MON","netPrice":"5.21","vatCategory":"S","vatRate":"19"},{"id":"4","name":"Servicegebühren D","quantity":"1","unitCode":"MON","netPrice":"282.36","vatCategory":"S","vatRate":"19"},{"id":"5","name":"Servicegebühren E","quantity":"1","unitCode":"MON","netPrice":"386.68","vatCategory":"S","vatRate":"19"},{"id":"6","name":"Servicegebühren F","quantity":"1","unitCode":"MON","netPrice":"535.11","vatCategory":"S","vatRate":"19"},{"id":"7","name":"Servicegebühren G","quantity":"1","unitCode":"MON","netPrice":"306.05","vatCategory":"S","vatRate":"19"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["lines.2.vatRate",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.vatCategory",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.id",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.name",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.quantity",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.unitCode",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.2.netPrice",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["lines.3.vatRate",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.vatCategory",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.id",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.name",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.quantity",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.unitCode",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.3.netPrice",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["lines.4.vatRate",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.vatCategory",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.id",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.name",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.quantity",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.unitCode",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.4.netPrice",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["lines.5.vatRate",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.vatCategory",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.id",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.name",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.quantity",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.unitCode",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.5.netPrice",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["lines.6.vatRate",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.vatCategory",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.id",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.name",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.quantity",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.unitCode",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["lines.6.netPrice",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Kranich Bürosysteme GmbH"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Hochschule Tannenberg"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer R123456789"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 06.07.2016"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 99000000-01944-98"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlungsbedingungen\nBitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital."}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRB 123456"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Hafenstraße 12\n12345 Lerchenau\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Hafenstraße 12\n12345 Lerchenau\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Hafenstraße 12\n12345 Lerchenau\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Hafenstraße 12\n12345 Lerchenau\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 18.236,72 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"USt 19 % (S) auf 18.236,72 € 3.464,98 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 21.701,70 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €"}],["documentTotals.lines.2",{"kind":"evidence","page":1,"quote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €"}],["documentTotals.lines.3",{"kind":"evidence","page":1,"quote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €"}],["documentTotals.lines.4",{"kind":"evidence","page":1,"quote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €"}],["documentTotals.lines.5",{"kind":"evidence","page":1,"quote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €"}],["documentTotals.lines.6",{"kind":"evidence","page":1,"quote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Kundenservice - Vertriebspartner Direktkunden"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: +49(0)12 3456-7890"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: kundenservice@email.de"}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[{"op":"add","path":"/seller/contact/name","value":"Kundenservice - Vertriebspartner Direktkunden","quote":"Ansprechpartner: Kundenservice - Vertriebspartner Direktkunden","page":1,"normalized":"Kundenservice - Vertriebspartner Direktkunden"},{"op":"add","path":"/seller/contact/phone","value":"+49(0)12 3456-7890","quote":"Telefon: +49(0)12 3456-7890","page":1,"normalized":"+49(0)12 3456-7890"},{"op":"add","path":"/seller/contact/email","value":"kundenservice@email.de","quote":"E-Mail: kundenservice@email.de","page":1,"normalized":"kundenservice@email.de"}],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"30aec56f4fcdd8637f06f23a28637dd4c1694108a750077ae38621a9d576444b","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"18236.72","taxTotal":"3464.98","grandTotal":"21701.70"},"documentTotals":{"lineTotal":"18236.72","taxTotal":"3464.98","grandTotal":"21701.70","lines":["17.24","16704.07","5.21","282.36","386.68","535.11","306.05"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n R123456789\n 380\n \n 20160706\n \n \n \n \n \n 1\n \n \n Servicegebühren A\n \n \n \n 17.24\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 17.24\n \n \n \n \n \n 2\n \n \n Servicegebühren B\n \n \n \n 16704.07\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 16704.07\n \n \n \n \n \n 3\n \n \n Servicegebühren C\n \n \n \n 5.21\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 5.21\n \n \n \n \n \n 4\n \n \n Servicegebühren D\n \n \n \n 282.36\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 282.36\n \n \n \n \n \n 5\n \n \n Servicegebühren E\n \n \n \n 386.68\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 386.68\n \n \n \n \n \n 6\n \n \n Servicegebühren F\n \n \n \n 535.11\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 535.11\n \n \n \n \n \n 7\n \n \n Servicegebühren G\n \n \n \n 306.05\n \n \n \n 1\n \n \n \n VAT\n S\n 19\n \n \n 306.05\n \n \n \n \n 99000000-01944-98\n \n Kranich Bürosysteme GmbH\n \n HRB 123456\n \n \n Kundenservice - Vertriebspartner Direktkunden\n \n +49(0)12 3456-7890\n \n \n kundenservice@email.de\n \n \n \n 12345\n Am Markt 3\n Eichstedt\n DE\n \n \n seller@email.de\n \n \n DE123456789\n \n \n \n Hochschule Tannenberg\n \n 12345\n Hafenstraße 12\n Lerchenau\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n \n \n \n 3464.98\n VAT\n 18236.72\n S\n 19\n \n \n Bitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital.\n \n \n 18236.72\n 18236.72\n 3464.98\n 21701.70\n 21701.70\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T21:22:26.509Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:bbf34fa5b319d9ec2bed02bc9eae6508b3b4d1e08c14afd7c202a9d85dd88bae\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:e21a7859c0b5e907b30e47939d518d5d6d5e1076a718c9668b2e9484539c3879\"}\n{\"seq\":1,\"ts\":\"2026-10-02T21:22:34.776Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:e691686614ceb15c40b605eaaca66a0d818c42ad5c7ef345fe3d0321f0eebd65\",\"promptTokens\":1515,\"completionTokens\":2119,\"costUsd\":\"0.00236160\",\"latencyMs\":8261},\"prevHash\":\"sha256:e21a7859c0b5e907b30e47939d518d5d6d5e1076a718c9668b2e9484539c3879\",\"hash\":\"sha256:b267cf5c04864b1aad79730e0959249bcf7ae7f8959afe2077a28abaed7e7e77\"}\n{\"seq\":2,\"ts\":\"2026-10-02T21:22:34.788Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":83,\"rejected\":[]},\"prevHash\":\"sha256:b267cf5c04864b1aad79730e0959249bcf7ae7f8959afe2077a28abaed7e7e77\",\"hash\":\"sha256:8efccdfab38d9ad6dca9f8dec76e6b9a39632b1ecef3b7c473b5cbbf41f78d73\"}\n{\"seq\":3,\"ts\":\"2026-10-02T21:22:35.802Z\",\"type\":\"REPAIR_PLAN\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/Nemotron-3-Ultra-550b-a55b\"},\"data\":{\"promptHash\":\"sha256:b61cd5b1c4bf85408a108c922af8fdea016b5a1d7c7d9303220a7a6aa929faad\",\"promptTokens\":1905,\"completionTokens\":271,\"costUsd\":\"0.00271800\",\"latencyMs\":1011},\"prevHash\":\"sha256:8efccdfab38d9ad6dca9f8dec76e6b9a39632b1ecef3b7c473b5cbbf41f78d73\",\"hash\":\"sha256:8d786b55ba367c907bd7a7ce1e458183433c6ff8f6e6335fde5f357630ec5ac6\"}\n{\"seq\":4,\"ts\":\"2026-10-02T21:22:35.804Z\",\"type\":\"PATCH_GUARD\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":[\"/seller/contact/name\",\"/seller/contact/phone\",\"/seller/contact/email\"],\"rejected\":[],\"unresolvable\":[]},\"prevHash\":\"sha256:8d786b55ba367c907bd7a7ce1e458183433c6ff8f6e6335fde5f357630ec5ac6\",\"hash\":\"sha256:a89c507e899fa6eb547643afcdc2ef2a22de160e77140125f9563157fc7784f4\"}\n{\"seq\":5,\"ts\":\"2026-10-02T21:22:35.805Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":7,\"vatGroups\":1},\"prevHash\":\"sha256:a89c507e899fa6eb547643afcdc2ef2a22de160e77140125f9563157fc7784f4\",\"hash\":\"sha256:3d5d92f6c9300759751ff1a55d3ee735c548bdf85b56c372c635c337179b1679\"}\n{\"seq\":6,\"ts\":\"2026-10-02T21:22:35.806Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:3d5d92f6c9300759751ff1a55d3ee735c548bdf85b56c372c635c337179b1679\",\"hash\":\"sha256:e34e7d11db2c6fa0e6418e87b255d57ec34ac80a2b2335c3c4f7cfca3c0732b8\"}\n{\"seq\":7,\"ts\":\"2026-10-02T21:22:35.807Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241\",\"data\":{},\"prevHash\":\"sha256:e34e7d11db2c6fa0e6418e87b255d57ec34ac80a2b2335c3c4f7cfca3c0732b8\",\"hash\":\"sha256:79d3e6be9df9ff08451c08175ac671e7c9bab2a9862531e71075c3ad707d5843\"}\n{\"seq\":8,\"ts\":\"2026-10-02T21:22:35.892Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"30aec56f4fcdd8637f06f23a28637dd4c1694108a750077ae38621a9d576444b\"},\"prevHash\":\"sha256:79d3e6be9df9ff08451c08175ac671e7c9bab2a9862531e71075c3ad707d5843\",\"hash\":\"sha256:6a5ac1742b3160e0f8d80c130a1a863b796229e9c124bf1789930e8788678cd6\"}\n{\"seq\":9,\"ts\":\"2026-10-02T21:22:35.893Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:20261f6bcdab113cc0eda3e1500dcd80140eae4aa7df41837de41caf0ea0f241\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:6a5ac1742b3160e0f8d80c130a1a863b796229e9c124bf1789930e8788678cd6\",\"hash\":\"sha256:6dff9185e01bc2b01996e76d34a45cab74c85390009d273adb0a23a9f29d3d02\"}\n","iterations":1,"costUsd":"0.005080","extraction":{"header":{"invoiceNumber":{"value":"R123456789","quote":"Rechnungsnummer R123456789","page":1},"issueDate":{"value":"06.07.2016","quote":"Rechnungsdatum 06.07.2016","page":1},"dueDate":null,"buyerReference":{"value":"99000000-01944-98","quote":"Leitweg-ID / Käuferreferenz 99000000-01944-98","page":1},"orderReference":null,"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"Bitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital.","quote":"Zahlungsbedingungen\nBitte überweisen Sie den Gesamtbetrag in Höhe von EUR Fachzeitschrift Verwaltung digital innerhalb von Fachzeitschrift\nVerwaltung digital Tagen ohne Abzug auf unser Konto IBAN Fachzeitschrift Verwaltung digital bei Fachzeitschrift\nVerwaltung digital BIC Fachzeitschrift Verwaltung digital.","page":1}},"seller":{"name":{"value":"Kranich Bürosysteme GmbH","quote":"Kranich Bürosysteme GmbH","page":1},"street":{"value":"Am Markt 3","quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE","page":1},"addressLine2":null,"city":{"value":"Eichstedt","quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE","page":1},"postcode":{"value":"12345","quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE","page":1},"country":{"value":"DE","quote":"Kranich Bürosysteme GmbH · Am Markt 3 · 12345 Eichstedt · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":null,"legalRegistrationId":{"value":"HRB 123456","quote":"Handelsregister: HRB 123456","page":1},"vatId":{"value":"DE123456789","quote":"USt-IdNr.: DE123456789","page":1},"taxNumber":null,"contactName":null,"contactPhone":null,"contactEmail":null},"buyer":{"name":{"value":"Hochschule Tannenberg","quote":"Hochschule Tannenberg","page":1},"street":{"value":"Hafenstraße 12","quote":"Hafenstraße 12\n12345 Lerchenau\nDE","page":1},"addressLine2":null,"city":{"value":"Lerchenau","quote":"Hafenstraße 12\n12345 Lerchenau\nDE","page":1},"postcode":{"value":"12345","quote":"Hafenstraße 12\n12345 Lerchenau\nDE","page":1},"country":{"value":"DE","quote":"Hafenstraße 12\n12345 Lerchenau\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":null},"vatExemptions":[],"lines":[{"rowQuote":"1 Servicegebühren A 1 Monat (MON) 17,24 € 19 % (S) 17,24 €","page":1,"lineId":"1","name":"Servicegebühren A","quantity":"1","unit":"Monat (MON)","netPrice":"17,24 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"17,24 €"},{"rowQuote":"2 Servicegebühren B 1 Monat (MON) 16.704,07 € 19 % (S) 16.704,07 €","page":1,"lineId":"2","name":"Servicegebühren B","quantity":"1","unit":"Monat (MON)","netPrice":"16.704,07 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"16.704,07 €"},{"rowQuote":"3 Servicegebühren C 1 Monat (MON) 5,21 € 19 % (S) 5,21 €","page":1,"lineId":"3","name":"Servicegebühren C","quantity":"1","unit":"Monat (MON)","netPrice":"5,21 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"5,21 €"},{"rowQuote":"4 Servicegebühren D 1 Monat (MON) 282,36 € 19 % (S) 282,36 €","page":1,"lineId":"4","name":"Servicegebühren D","quantity":"1","unit":"Monat (MON)","netPrice":"282,36 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"282,36 €"},{"rowQuote":"5 Servicegebühren E 1 Monat (MON) 386,68 € 19 % (S) 386,68 €","page":1,"lineId":"5","name":"Servicegebühren E","quantity":"1","unit":"Monat (MON)","netPrice":"386,68 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"386,68 €"},{"rowQuote":"6 Servicegebühren F 1 Monat (MON) 535,11 € 19 % (S) 535,11 €","page":1,"lineId":"6","name":"Servicegebühren F","quantity":"1","unit":"Monat (MON)","netPrice":"535,11 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"535,11 €"},{"rowQuote":"7 Servicegebühren G 1 Monat (MON) 306,05 € 19 % (S) 306,05 €","page":1,"lineId":"7","name":"Servicegebühren G","quantity":"1","unit":"Monat (MON)","netPrice":"306,05 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"306,05 €"}],"documentTotals":{"lineTotal":{"value":"18.236,72 €","quote":"Summe netto 18.236,72 €","page":1},"taxTotal":{"value":"3.464,98 €","quote":"USt 19 % (S) auf 18.236,72 € 3.464,98 €","page":1},"grandTotal":{"value":"21.701,70 €","quote":"Rechnungsbetrag 21.701,70 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/seven-lines.pdf b/apps/web/public/gallery/seven-lines.pdf new file mode 100644 index 0000000..8877c2d Binary files /dev/null and b/apps/web/public/gallery/seven-lines.pdf differ diff --git a/apps/web/public/gallery/total-off.json b/apps/web/public/gallery/total-off.json new file mode 100644 index 0000000..18e7e2c --- /dev/null +++ b/apps/web/public/gallery/total-off.json @@ -0,0 +1 @@ +{"id":"total-off","title":"Printed total is wrong","story":"The document adds up to 1.00 less than it claims. Verdict trusts the lines, computes the total and flags the difference.","pdf":"total-off.pdf","synthetic":true,"run":[{"at":12,"event":{"type":"start","pages":["Testverkäufer\nTestverkäufer · Teststraße · 12345 Testhausen · DE\nTestkäufer\nTeststraße\n12345 Testhausen\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer 112233\nRechnungsdatum 23.04.2021\nFälligkeitsdatum 28.04.2021\nLeitweg-ID / Käuferreferenz 991-33333TEST-33\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €\n2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €\n3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €\n4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €\nSumme netto 1.500,00 €\nUSt 19 % (S) auf 1.600,00 € 304,00 €\nUSt 0 % (Z) auf -100,00 € 0,00 €\nUmsatzsteuer 304,00 €\nRechnungsbetrag 1.805,00 €\nTestverkäufer\nTeststraße\n12345 Testhausen\nDE\nAnsprechpartner: Test\nTelefon: 123456789\nE-Mail: test@test.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nKontoinhaber: Testkäufer\nUSt-IdNr.: DE12345464867\nSteuernummer: 1121081508150\nVerkäufer-Kennung: 116078\nSeite 1 von 1"],"uploadHash":"sha256:9a425c9d9491bccfa095bb7461c34488f70041d3de38d2bb6af2a28736d54a01"}},{"at":12,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:55:44.654Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:9a425c9d9491bccfa095bb7461c34488f70041d3de38d2bb6af2a28736d54a01","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:74bb6e5e459e2b3b5b230b8a62c09db1c144e7fd8dfca50f6e7d7f1233f65721"}}},{"at":6837,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:55:51.479Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:bf62dae0daec703a8acdb29fc74e511201ac29c129ec26d943e3f051b52b098d","promptTokens":1412,"completionTokens":1815,"costUsd":"0.00205710","latencyMs":6823},"prevHash":"sha256:74bb6e5e459e2b3b5b230b8a62c09db1c144e7fd8dfca50f6e7d7f1233f65721","hash":"sha256:db4c09b9673f57c5ce39bf5e8a91aa3b7ea792f9ea645dcef006c955b574d640"}}},{"at":6840,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:55:51.482Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":64,"rejected":[]},"prevHash":"sha256:db4c09b9673f57c5ce39bf5e8a91aa3b7ea792f9ea645dcef006c955b574d640","hash":"sha256:9add47a311705f092fc2d42148dd080c7f2de52186c2101bf5e0df8886e22703"}}},{"at":6840,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:55:51.483Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":4,"vatGroups":2},"prevHash":"sha256:9add47a311705f092fc2d42148dd080c7f2de52186c2101bf5e0df8886e22703","hash":"sha256:72decee3135f719bf2c89f8a01ec5543a5a3d1ba1b480f8df6644a14bd7d7e16"}}},{"at":6840,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:55:51.483Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":1,"lineMismatches":0},"prevHash":"sha256:72decee3135f719bf2c89f8a01ec5543a5a3d1ba1b480f8df6644a14bd7d7e16","hash":"sha256:37bfd4ef16691ddc4314feb675611e7fffe8919dcde5e37bc022681459fba24a"}}},{"at":6841,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:55:51.483Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82","data":{},"prevHash":"sha256:37bfd4ef16691ddc4314feb675611e7fffe8919dcde5e37bc022681459fba24a","hash":"sha256:99b73a37386c0e03467a83943e44a7b228db3526bfc9c86af3c27b67904cac32"}}},{"at":6873,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T20:55:51.516Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82","data":{"valid":true,"errorCount":0,"reportHash":"fba4d80d0f249d4d81f2a45f8de3e2b36356da202073e08d268e6b9d61d15f6c"},"prevHash":"sha256:99b73a37386c0e03467a83943e44a7b228db3526bfc9c86af3c27b67904cac32","hash":"sha256:b483089607facede20b31de87fba4031c99708b7f424a538830447748320b139"}}},{"at":6874,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T20:55:51.516Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82"},"fieldsWithoutProvenance":0},"prevHash":"sha256:b483089607facede20b31de87fba4031c99708b7f424a538830447748320b139","hash":"sha256:fe3e67c2d994c3ac269920bf523d84fd606aaf5b9962baae6a7a2bd828546c98"}}},{"at":6874,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"112233","issueDate":"2021-04-23","typeCode":"380","currency":"EUR","dueDate":"2021-04-28","buyerReference":"991-33333TEST-33","seller":{"id":"116078","name":"Testverkäufer","vatId":"DE12345464867","taxNumber":"1121081508150","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Teststraße","city":"Testhausen","postcode":"12345","countryCode":"DE"},"contact":{"name":"Test","phone":"123456789","email":"test@test.de"}},"buyer":{"name":"Testkäufer","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Teststraße","city":"Testhausen","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890","accountName":"Testkäufer"},"vatExemptions":[],"lines":[{"id":"1","name":"Neumotor","quantity":"1.00","unitCode":"C62","netPrice":"1000.00","vatCategory":"S","vatRate":"19.00"},{"id":"2","name":"Bemessungsgrundlage und Umsatzsteuer auf Altteil","quantity":"1.00","unitCode":"C62","netPrice":"100.00","vatCategory":"S","vatRate":"19.00"},{"id":"3","name":"Korrektur/Stornierung Bemessungsgrundlage der Umsatzsteuer auf Altteil","quantity":"-1.00","unitCode":"C62","netPrice":"100.00","vatCategory":"Z","vatRate":"0.00"},{"id":"4","name":"Montage","quantity":"1.00","unitCode":"HUR","netPrice":"500.00","vatCategory":"S","vatRate":"19.00"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["lines.2.vatRate",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.vatCategory",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.id",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.name",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.quantity",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.unitCode",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.2.netPrice",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["lines.3.vatRate",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.vatCategory",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.id",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.name",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.quantity",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.unitCode",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["lines.3.netPrice",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Testverkäufer\nTestverkäufer · Teststraße · 12345 Testhausen · DE"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Testkäufer\nTeststraße\n12345 Testhausen\nDE"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer 112233"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 23.04.2021"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["dueDate",{"kind":"evidence","page":1,"quote":"Fälligkeitsdatum 28.04.2021"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 991-33333TEST-33"}],["seller.id",{"kind":"evidence","page":1,"quote":"Verkäufer-Kennung: 116078"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE12345464867"}],["seller.taxNumber",{"kind":"evidence","page":1,"quote":"Steuernummer: 1121081508150"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Teststraße"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"12345 Testhausen"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"12345 Testhausen"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"12345 Testhausen · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Test"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: 123456789"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: test@test.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Teststraße"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"12345 Testhausen"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"12345 Testhausen"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"DE"}],["payment.accountName",{"kind":"evidence","page":1,"quote":"Kontoinhaber: Testkäufer"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 1.500,00 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"Umsatzsteuer 304,00 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 1.805,00 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €"}],["documentTotals.lines.2",{"kind":"evidence","page":1,"quote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €"}],["documentTotals.lines.3",{"kind":"evidence","page":1,"quote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €"}]],"missing":[],"openIssues":[],"warnings":["BT-112: printed 1805.00, computed 1804.00"],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"fba4d80d0f249d4d81f2a45f8de3e2b36356da202073e08d268e6b9d61d15f6c","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"1500.00","taxTotal":"304.00","grandTotal":"1804.00"},"documentTotals":{"lineTotal":"1500.00","taxTotal":"304.00","grandTotal":"1805.00","lines":["1000.00","100.00","-100.00","500.00"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n 112233\n 380\n \n 20210423\n \n \n \n \n \n 1\n \n \n Neumotor\n \n \n \n 1000.00\n \n \n \n 1.00\n \n \n \n VAT\n S\n 19.00\n \n \n 1000.00\n \n \n \n \n \n 2\n \n \n Bemessungsgrundlage und Umsatzsteuer auf Altteil\n \n \n \n 100.00\n \n \n \n 1.00\n \n \n \n VAT\n S\n 19.00\n \n \n 100.00\n \n \n \n \n \n 3\n \n \n Korrektur/Stornierung Bemessungsgrundlage der Umsatzsteuer auf Altteil\n \n \n \n 100.00\n \n \n \n -1.00\n \n \n \n VAT\n Z\n 0.00\n \n \n -100.00\n \n \n \n \n \n 4\n \n \n Montage\n \n \n \n 500.00\n \n \n \n 1.00\n \n \n \n VAT\n S\n 19.00\n \n \n 500.00\n \n \n \n \n 991-33333TEST-33\n \n 116078\n Testverkäufer\n \n Test\n \n 123456789\n \n \n test@test.de\n \n \n \n 12345\n Teststraße\n Testhausen\n DE\n \n \n seller@email.de\n \n \n 1121081508150\n \n \n DE12345464867\n \n \n \n Testkäufer\n \n 12345\n Teststraße\n Testhausen\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n Testkäufer\n \n \n \n 304.00\n VAT\n 1600.00\n S\n 19\n \n \n 0.00\n VAT\n -100.00\n Z\n 0\n \n \n \n 20210428\n \n \n \n 1500.00\n 1500.00\n 304.00\n 1804.00\n 1804.00\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:55:44.654Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:9a425c9d9491bccfa095bb7461c34488f70041d3de38d2bb6af2a28736d54a01\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:74bb6e5e459e2b3b5b230b8a62c09db1c144e7fd8dfca50f6e7d7f1233f65721\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:55:51.479Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:bf62dae0daec703a8acdb29fc74e511201ac29c129ec26d943e3f051b52b098d\",\"promptTokens\":1412,\"completionTokens\":1815,\"costUsd\":\"0.00205710\",\"latencyMs\":6823},\"prevHash\":\"sha256:74bb6e5e459e2b3b5b230b8a62c09db1c144e7fd8dfca50f6e7d7f1233f65721\",\"hash\":\"sha256:db4c09b9673f57c5ce39bf5e8a91aa3b7ea792f9ea645dcef006c955b574d640\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:55:51.482Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":64,\"rejected\":[]},\"prevHash\":\"sha256:db4c09b9673f57c5ce39bf5e8a91aa3b7ea792f9ea645dcef006c955b574d640\",\"hash\":\"sha256:9add47a311705f092fc2d42148dd080c7f2de52186c2101bf5e0df8886e22703\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:55:51.483Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":4,\"vatGroups\":2},\"prevHash\":\"sha256:9add47a311705f092fc2d42148dd080c7f2de52186c2101bf5e0df8886e22703\",\"hash\":\"sha256:72decee3135f719bf2c89f8a01ec5543a5a3d1ba1b480f8df6644a14bd7d7e16\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:55:51.483Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":1,\"lineMismatches\":0},\"prevHash\":\"sha256:72decee3135f719bf2c89f8a01ec5543a5a3d1ba1b480f8df6644a14bd7d7e16\",\"hash\":\"sha256:37bfd4ef16691ddc4314feb675611e7fffe8919dcde5e37bc022681459fba24a\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:55:51.483Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82\",\"data\":{},\"prevHash\":\"sha256:37bfd4ef16691ddc4314feb675611e7fffe8919dcde5e37bc022681459fba24a\",\"hash\":\"sha256:99b73a37386c0e03467a83943e44a7b228db3526bfc9c86af3c27b67904cac32\"}\n{\"seq\":6,\"ts\":\"2026-10-02T20:55:51.516Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"fba4d80d0f249d4d81f2a45f8de3e2b36356da202073e08d268e6b9d61d15f6c\"},\"prevHash\":\"sha256:99b73a37386c0e03467a83943e44a7b228db3526bfc9c86af3c27b67904cac32\",\"hash\":\"sha256:b483089607facede20b31de87fba4031c99708b7f424a538830447748320b139\"}\n{\"seq\":7,\"ts\":\"2026-10-02T20:55:51.516Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:244e6da3bef89baee4286333b322d5fbba990b5b23eb9a63407fcaa1b6ebce82\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:b483089607facede20b31de87fba4031c99708b7f424a538830447748320b139\",\"hash\":\"sha256:fe3e67c2d994c3ac269920bf523d84fd606aaf5b9962baae6a7a2bd828546c98\"}\n","iterations":0,"costUsd":"0.002057","extraction":{"header":{"invoiceNumber":{"value":"112233","quote":"Rechnungsnummer 112233","page":1},"issueDate":{"value":"23.04.2021","quote":"Rechnungsdatum 23.04.2021","page":1},"dueDate":{"value":"28.04.2021","quote":"Fälligkeitsdatum 28.04.2021","page":1},"buyerReference":{"value":"991-33333TEST-33","quote":"Leitweg-ID / Käuferreferenz 991-33333TEST-33","page":1},"orderReference":{"value":"","quote":"","page":1},"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"","quote":"","page":1}},"seller":{"name":{"value":"Testverkäufer","quote":"Testverkäufer\nTestverkäufer · Teststraße · 12345 Testhausen · DE","page":1},"street":{"value":"Teststraße","quote":"Teststraße","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Testhausen","quote":"12345 Testhausen","page":1},"postcode":{"value":"12345","quote":"12345 Testhausen","page":1},"country":{"value":"DE","quote":"12345 Testhausen · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":{"value":"116078","quote":"Verkäufer-Kennung: 116078","page":1},"legalRegistrationId":{"value":"","quote":"","page":1},"vatId":{"value":"DE12345464867","quote":"USt-IdNr.: DE12345464867","page":1},"taxNumber":{"value":"1121081508150","quote":"Steuernummer: 1121081508150","page":1},"contactName":{"value":"Test","quote":"Ansprechpartner: Test","page":1},"contactPhone":{"value":"123456789","quote":"Telefon: 123456789","page":1},"contactEmail":{"value":"test@test.de","quote":"E-Mail: test@test.de","page":1}},"buyer":{"name":{"value":"Testkäufer","quote":"Testkäufer\nTeststraße\n12345 Testhausen\nDE","page":1},"street":{"value":"Teststraße","quote":"Teststraße","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Testhausen","quote":"12345 Testhausen","page":1},"postcode":{"value":"12345","quote":"12345 Testhausen","page":1},"country":{"value":"DE","quote":"DE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":{"value":"Testkäufer","quote":"Kontoinhaber: Testkäufer","page":1}},"vatExemptions":[],"lines":[{"rowQuote":"1 Neumotor 1,00 Stk. (C62) 1.000,00 € 19,00 % (S) 1.000,00 €","page":1,"lineId":"1","name":"Neumotor","quantity":"1,00","unit":"Stk. (C62)","netPrice":"1.000,00 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19,00 %","lineAmount":"1.000,00 €"},{"rowQuote":"2 Bemessungsgrundlage und\nUmsatzsteuer auf Altteil\n1,00 Stk. (C62) 100,00 € 19,00 % (S) 100,00 €","page":1,"lineId":"2","name":"Bemessungsgrundlage und\nUmsatzsteuer auf Altteil","quantity":"1,00","unit":"Stk. (C62)","netPrice":"100,00 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19,00 %","lineAmount":"100,00 €"},{"rowQuote":"3 Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil\n-1,00 Stk. (C62) 100,00 € 0,00 % (Z) -100,00 €","page":1,"lineId":"3","name":"Korrektur/Stornierung\nBemessungsgrundlage der\nUmsatzsteuer auf Altteil","quantity":"-1,00","unit":"Stk. (C62)","netPrice":"100,00 €","priceBaseQuantity":null,"vatCategory":"(Z)","vatRate":"0,00 %","lineAmount":"-100,00 €"},{"rowQuote":"4 Montage 1,00 Std. (HUR) 500,00 € 19,00 % (S) 500,00 €","page":1,"lineId":"4","name":"Montage","quantity":"1,00","unit":"Std. (HUR)","netPrice":"500,00 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19,00 %","lineAmount":"500,00 €"}],"documentTotals":{"lineTotal":{"value":"1.500,00 €","quote":"Summe netto 1.500,00 €","page":1},"taxTotal":{"value":"304,00 €","quote":"Umsatzsteuer 304,00 €","page":1},"grandTotal":{"value":"1.805,00 €","quote":"Rechnungsbetrag 1.805,00 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/total-off.pdf b/apps/web/public/gallery/total-off.pdf new file mode 100644 index 0000000..2b14a33 Binary files /dev/null and b/apps/web/public/gallery/total-off.pdf differ diff --git a/apps/web/public/gallery/unit-word.json b/apps/web/public/gallery/unit-word.json new file mode 100644 index 0000000..a978e19 --- /dev/null +++ b/apps/web/public/gallery/unit-word.json @@ -0,0 +1 @@ +{"id":"unit-word","title":"Units printed as words","story":"\"Stück\" is not a unit code. KoSIT reports it as an error yet still accepts the invoice; Verdict normalizes it to H87.","pdf":"unit-word.pdf","synthetic":true,"run":[{"at":20,"event":{"type":"start","pages":["Elbtal Medienservice UG\nElbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE\nZentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE\nElektronische Adresse: buyer@info.de (Schema: EM)\nRechnungsnummer PRG1502112\nRechnungsdatum 24.04.2015\nLeitweg-ID / Käuferreferenz 99000000-18188-18\nWährung EUR\nRechnung\nPos. Bezeichnung Menge Einheit Einzelpreis USt Betrag\n1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €\n2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €\n3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €\n4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €\nSumme netto 8.870,00 €\nUSt 19 % (S) auf 8.870,00 € 1.685,30 €\nUmsatzsteuer 1.685,30 €\nRechnungsbetrag 10.555,30 €\nZahlungsbedingungen\n14 Tage\nElbtal Medienservice UG\nHafenstraße 12\n12345 Lerchenau\nDE\nAnsprechpartner: Murat Yilmaz\nTelefon: +49 123456789\nE-Mail: xxx@schulung.de\nElektronische Adresse: seller@email.de (Schema:\nEM)\nIBAN: DE79000000001234567890\nUSt-IdNr.: DE123456789\nHandelsregister: HRB 123456\nSeite 1 von 1"],"uploadHash":"sha256:6d6624f912d3c5d43f207f884fd800d07ee6aabcb030825cf87b6964f8fc4251"}},{"at":20,"event":{"type":"audit","event":{"seq":0,"ts":"2026-10-02T20:55:37.727Z","type":"INGEST","actor":{"kind":"core","version":"0.1.0"},"inputHash":"sha256:6d6624f912d3c5d43f207f884fd800d07ee6aabcb030825cf87b6964f8fc4251","data":{"pages":1},"prevHash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","hash":"sha256:4e63816c3e4ecf45426067e7ae071e03a6962298ce3836a9aebc54a642273d0d"}}},{"at":6874,"event":{"type":"audit","event":{"seq":1,"ts":"2026-10-02T20:55:44.581Z","type":"EXTRACT","actor":{"kind":"model","model":"nvidia/nemotron-3-super-120b-a12b"},"data":{"promptHash":"sha256:01db7fd401034ab353448bc1bced84e446ecbb8e85feac6990f634cff832f51a","promptTokens":1317,"completionTokens":1835,"costUsd":"0.00204660","latencyMs":6853},"prevHash":"sha256:4e63816c3e4ecf45426067e7ae071e03a6962298ce3836a9aebc54a642273d0d","hash":"sha256:7c58907dcfb21377c63c354ebeaa72d409a67f2e61377674b0229eb908d92591"}}},{"at":6877,"event":{"type":"audit","event":{"seq":2,"ts":"2026-10-02T20:55:44.584Z","type":"EVIDENCE_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"accepted":62,"rejected":[]},"prevHash":"sha256:7c58907dcfb21377c63c354ebeaa72d409a67f2e61377674b0229eb908d92591","hash":"sha256:47b2672ae5ed1aeeba5ae0a0bc23a552124edd76ab10ae636d5d81e8d396b311"}}},{"at":6877,"event":{"type":"audit","event":{"seq":3,"ts":"2026-10-02T20:55:44.584Z","type":"DERIVE","actor":{"kind":"core","version":"0.1.0"},"data":{"lines":4,"vatGroups":1},"prevHash":"sha256:47b2672ae5ed1aeeba5ae0a0bc23a552124edd76ab10ae636d5d81e8d396b311","hash":"sha256:21eddba488efdb0dc9290c997974714b198e2d79ebe8e20babb0044ec97029ab"}}},{"at":6877,"event":{"type":"audit","event":{"seq":4,"ts":"2026-10-02T20:55:44.584Z","type":"CONSISTENCY_CHECK","actor":{"kind":"core","version":"0.1.0"},"data":{"totalWarnings":0,"lineMismatches":0},"prevHash":"sha256:21eddba488efdb0dc9290c997974714b198e2d79ebe8e20babb0044ec97029ab","hash":"sha256:5b8726de688012867e8f26ad5e1b84c2ab9ce0fc3bb46ec9a33d0d91a7fe7a96"}}},{"at":6877,"event":{"type":"audit","event":{"seq":5,"ts":"2026-10-02T20:55:44.584Z","type":"BUILD_CII","actor":{"kind":"core","version":"0.1.0"},"outputHash":"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13","data":{},"prevHash":"sha256:5b8726de688012867e8f26ad5e1b84c2ab9ce0fc3bb46ec9a33d0d91a7fe7a96","hash":"sha256:becc8214f88b81e6c6777ab828839535b6d0d2e176cef979b5feb1f438469d3d"}}},{"at":6932,"event":{"type":"audit","event":{"seq":6,"ts":"2026-10-02T20:55:44.640Z","type":"VALIDATE","actor":{"kind":"verifier","version":"1.6.3","config":"xrechnung-3.0.2-validator-configuration-2026-08-31"},"inputHash":"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13","data":{"valid":true,"errorCount":0,"reportHash":"6f34dac357bbcdd3b23fef2f429561785227e06704124597910e4ddf26881f48"},"prevHash":"sha256:becc8214f88b81e6c6777ab828839535b6d0d2e176cef979b5feb1f438469d3d","hash":"sha256:16ea5caa025ef9a733b33ca99ae53d1ebe576d04eb34b4c1c4fee7ae1589615a"}}},{"at":6933,"event":{"type":"audit","event":{"seq":7,"ts":"2026-10-02T20:55:44.640Z","type":"OUTPUT","actor":{"kind":"core","version":"0.1.0"},"data":{"files":{"xrechnung.xml":"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13"},"fieldsWithoutProvenance":0},"prevHash":"sha256:16ea5caa025ef9a733b33ca99ae53d1ebe576d04eb34b4c1c4fee7ae1589615a","hash":"sha256:28c15ee654954265e18884cd181ea9a0464bf69086bd146ea544d8114a266db2"}}},{"at":6933,"event":{"type":"result","result":{"state":"OUTPUT","invoice":{"number":"PRG1502112","issueDate":"2015-04-24","typeCode":"380","currency":"EUR","buyerReference":"99000000-18188-18","paymentTerms":"14 Tage","seller":{"name":"Elbtal Medienservice UG","legalRegistrationId":"HRB 123456","vatId":"DE123456789","electronicAddress":{"scheme":"EM","value":"seller@email.de"},"address":{"line1":"Hafenstraße 12","city":"Lerchenau","postcode":"12345","countryCode":"DE"},"contact":{"name":"Murat Yilmaz","phone":"+49 123456789","email":"xxx@schulung.de"}},"buyer":{"name":"Zentrale Vergabestelle Eichstedt","electronicAddress":{"scheme":"EM","value":"buyer@info.de"},"address":{"line1":"Bahnhofstraße 5a","city":"Ahornhain","postcode":"12345","countryCode":"DE"}},"payment":{"meansCode":"58","iban":"DE79000000001234567890"},"vatExemptions":[],"lines":[{"id":"1","name":"Seminar","quantity":"5","unitCode":"H87","netPrice":"1340","vatCategory":"S","vatRate":"19"},{"id":"2","name":"Raumkosten","quantity":"5","unitCode":"HUR","netPrice":"300","vatCategory":"S","vatRate":"19"},{"id":"3","name":"Reisekostenpauschale","quantity":"5","unitCode":"H87","netPrice":"90","vatCategory":"S","vatRate":"19"},{"id":"4","name":"Seminarunterlagen","quantity":"11","unitCode":"HUR","netPrice":"20","vatCategory":"S","vatRate":"19"}]},"provenance":[["lines.0.vatRate",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.vatCategory",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.id",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.name",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.quantity",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.unitCode",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.0.netPrice",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["lines.1.vatRate",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.vatCategory",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.id",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.name",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.quantity",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.unitCode",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.1.netPrice",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["lines.2.vatRate",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.vatCategory",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.id",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.name",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.quantity",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.unitCode",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.2.netPrice",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["lines.3.vatRate",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.vatCategory",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.id",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.name",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.quantity",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.unitCode",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["lines.3.netPrice",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}],["seller.name",{"kind":"evidence","page":1,"quote":"Elbtal Medienservice UG"}],["buyer.name",{"kind":"evidence","page":1,"quote":"Zentrale Vergabestelle Eichstedt"}],["seller.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)"}],["seller.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["seller.electronicAddress.value"]}],["buyer.electronicAddress.value",{"kind":"evidence","page":1,"quote":"Elektronische Adresse: buyer@info.de (Schema: EM)"}],["buyer.electronicAddress.scheme",{"kind":"derived","rule":"electronic-address-scheme-from-format","inputs":["buyer.electronicAddress.value"]}],["payment.iban",{"kind":"evidence","page":1,"quote":"IBAN: DE79000000001234567890"}],["number",{"kind":"evidence","page":1,"quote":"Rechnungsnummer PRG1502112"}],["issueDate",{"kind":"evidence","page":1,"quote":"Rechnungsdatum 24.04.2015"}],["currency",{"kind":"evidence","page":1,"quote":"Währung EUR"}],["buyerReference",{"kind":"evidence","page":1,"quote":"Leitweg-ID / Käuferreferenz 99000000-18188-18"}],["paymentTerms",{"kind":"evidence","page":1,"quote":"Zahlungsbedingungen\n14 Tage"}],["seller.legalRegistrationId",{"kind":"evidence","page":1,"quote":"Handelsregister: HRB 123456"}],["seller.vatId",{"kind":"evidence","page":1,"quote":"USt-IdNr.: DE123456789"}],["seller.address.line1",{"kind":"evidence","page":1,"quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE"}],["seller.address.city",{"kind":"evidence","page":1,"quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE"}],["seller.address.postcode",{"kind":"evidence","page":1,"quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE"}],["seller.address.countryCode",{"kind":"evidence","page":1,"quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE"}],["seller.contact.name",{"kind":"evidence","page":1,"quote":"Ansprechpartner: Murat Yilmaz"}],["seller.contact.phone",{"kind":"evidence","page":1,"quote":"Telefon: +49 123456789"}],["seller.contact.email",{"kind":"evidence","page":1,"quote":"E-Mail: xxx@schulung.de"}],["buyer.address.line1",{"kind":"evidence","page":1,"quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE"}],["buyer.address.city",{"kind":"evidence","page":1,"quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE"}],["buyer.address.postcode",{"kind":"evidence","page":1,"quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE"}],["buyer.address.countryCode",{"kind":"evidence","page":1,"quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE"}],["payment.meansCode",{"kind":"derived","rule":"credit-transfer-from-iban","inputs":["payment.iban"]}],["typeCode",{"kind":"derived","rule":"mvp-commercial-invoice","inputs":[]}],["documentTotals.lineTotal",{"kind":"evidence","page":1,"quote":"Summe netto 8.870,00 €"}],["documentTotals.taxTotal",{"kind":"evidence","page":1,"quote":"Ust 19 % (S) auf 8.870,00 € 1.685,30 €\nUmsatzsteuer 1.685,30 €"}],["documentTotals.grandTotal",{"kind":"evidence","page":1,"quote":"Rechnungsbetrag 10.555,30 €"}],["documentTotals.lines.0",{"kind":"evidence","page":1,"quote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €"}],["documentTotals.lines.1",{"kind":"evidence","page":1,"quote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €"}],["documentTotals.lines.2",{"kind":"evidence","page":1,"quote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €"}],["documentTotals.lines.3",{"kind":"evidence","page":1,"quote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €"}]],"missing":[],"openIssues":[],"warnings":[],"rejectedFacts":[],"patchesApplied":[],"patchesRejected":[],"verifier":{"valid":true,"errors":[{"ruleId":"BR-DE-TMP-32","severity":"information","message":"[BR-DE-TMP-32] Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.","location":"/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice[1]/Q{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}SupplyChainTradeTransaction[1]"}],"reportHash":"6f34dac357bbcdd3b23fef2f429561785227e06704124597910e4ddf26881f48","versions":{"validator":"1.6.3","configuration":"xrechnung-3.0.2-validator-configuration-2026-08-31"}},"amounts":{"lineTotal":"8870.00","taxTotal":"1685.30","grandTotal":"10555.30"},"documentTotals":{"lineTotal":"8870.00","taxTotal":"1685.30","grandTotal":"10555.30","lines":["6700.00","1500.00","450.00","220.00"]},"xml":"\n\n \n \n urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\n \n \n urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0\n \n \n \n PRG1502112\n 380\n \n 20150424\n \n \n \n \n \n 1\n \n \n Seminar\n \n \n \n 1340\n \n \n \n 5\n \n \n \n VAT\n S\n 19\n \n \n 6700.00\n \n \n \n \n \n 2\n \n \n Raumkosten\n \n \n \n 300\n \n \n \n 5\n \n \n \n VAT\n S\n 19\n \n \n 1500.00\n \n \n \n \n \n 3\n \n \n Reisekostenpauschale\n \n \n \n 90\n \n \n \n 5\n \n \n \n VAT\n S\n 19\n \n \n 450.00\n \n \n \n \n \n 4\n \n \n Seminarunterlagen\n \n \n \n 20\n \n \n \n 11\n \n \n \n VAT\n S\n 19\n \n \n 220.00\n \n \n \n \n 99000000-18188-18\n \n Elbtal Medienservice UG\n \n HRB 123456\n \n \n Murat Yilmaz\n \n +49 123456789\n \n \n xxx@schulung.de\n \n \n \n 12345\n Hafenstraße 12\n Lerchenau\n DE\n \n \n seller@email.de\n \n \n DE123456789\n \n \n \n Zentrale Vergabestelle Eichstedt\n \n 12345\n Bahnhofstraße 5a\n Ahornhain\n DE\n \n \n buyer@info.de\n \n \n \n \n \n EUR\n \n 58\n \n DE79000000001234567890\n \n \n \n 1685.30\n VAT\n 8870.00\n S\n 19\n \n \n 14 Tage\n \n \n 8870.00\n 8870.00\n 1685.30\n 10555.30\n 10555.30\n \n \n \n\n","auditJsonl":"{\"seq\":0,\"ts\":\"2026-10-02T20:55:37.727Z\",\"type\":\"INGEST\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"inputHash\":\"sha256:6d6624f912d3c5d43f207f884fd800d07ee6aabcb030825cf87b6964f8fc4251\",\"data\":{\"pages\":1},\"prevHash\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"hash\":\"sha256:4e63816c3e4ecf45426067e7ae071e03a6962298ce3836a9aebc54a642273d0d\"}\n{\"seq\":1,\"ts\":\"2026-10-02T20:55:44.581Z\",\"type\":\"EXTRACT\",\"actor\":{\"kind\":\"model\",\"model\":\"nvidia/nemotron-3-super-120b-a12b\"},\"data\":{\"promptHash\":\"sha256:01db7fd401034ab353448bc1bced84e446ecbb8e85feac6990f634cff832f51a\",\"promptTokens\":1317,\"completionTokens\":1835,\"costUsd\":\"0.00204660\",\"latencyMs\":6853},\"prevHash\":\"sha256:4e63816c3e4ecf45426067e7ae071e03a6962298ce3836a9aebc54a642273d0d\",\"hash\":\"sha256:7c58907dcfb21377c63c354ebeaa72d409a67f2e61377674b0229eb908d92591\"}\n{\"seq\":2,\"ts\":\"2026-10-02T20:55:44.584Z\",\"type\":\"EVIDENCE_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"accepted\":62,\"rejected\":[]},\"prevHash\":\"sha256:7c58907dcfb21377c63c354ebeaa72d409a67f2e61377674b0229eb908d92591\",\"hash\":\"sha256:47b2672ae5ed1aeeba5ae0a0bc23a552124edd76ab10ae636d5d81e8d396b311\"}\n{\"seq\":3,\"ts\":\"2026-10-02T20:55:44.584Z\",\"type\":\"DERIVE\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"lines\":4,\"vatGroups\":1},\"prevHash\":\"sha256:47b2672ae5ed1aeeba5ae0a0bc23a552124edd76ab10ae636d5d81e8d396b311\",\"hash\":\"sha256:21eddba488efdb0dc9290c997974714b198e2d79ebe8e20babb0044ec97029ab\"}\n{\"seq\":4,\"ts\":\"2026-10-02T20:55:44.584Z\",\"type\":\"CONSISTENCY_CHECK\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"totalWarnings\":0,\"lineMismatches\":0},\"prevHash\":\"sha256:21eddba488efdb0dc9290c997974714b198e2d79ebe8e20babb0044ec97029ab\",\"hash\":\"sha256:5b8726de688012867e8f26ad5e1b84c2ab9ce0fc3bb46ec9a33d0d91a7fe7a96\"}\n{\"seq\":5,\"ts\":\"2026-10-02T20:55:44.584Z\",\"type\":\"BUILD_CII\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"outputHash\":\"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13\",\"data\":{},\"prevHash\":\"sha256:5b8726de688012867e8f26ad5e1b84c2ab9ce0fc3bb46ec9a33d0d91a7fe7a96\",\"hash\":\"sha256:becc8214f88b81e6c6777ab828839535b6d0d2e176cef979b5feb1f438469d3d\"}\n{\"seq\":6,\"ts\":\"2026-10-02T20:55:44.640Z\",\"type\":\"VALIDATE\",\"actor\":{\"kind\":\"verifier\",\"version\":\"1.6.3\",\"config\":\"xrechnung-3.0.2-validator-configuration-2026-08-31\"},\"inputHash\":\"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13\",\"data\":{\"valid\":true,\"errorCount\":0,\"reportHash\":\"6f34dac357bbcdd3b23fef2f429561785227e06704124597910e4ddf26881f48\"},\"prevHash\":\"sha256:becc8214f88b81e6c6777ab828839535b6d0d2e176cef979b5feb1f438469d3d\",\"hash\":\"sha256:16ea5caa025ef9a733b33ca99ae53d1ebe576d04eb34b4c1c4fee7ae1589615a\"}\n{\"seq\":7,\"ts\":\"2026-10-02T20:55:44.640Z\",\"type\":\"OUTPUT\",\"actor\":{\"kind\":\"core\",\"version\":\"0.1.0\"},\"data\":{\"files\":{\"xrechnung.xml\":\"sha256:4bd31f01e392f9ad8a02fda4ca2e8cea2aa457d804f0fce4a1481f1eb3fc5f13\"},\"fieldsWithoutProvenance\":0},\"prevHash\":\"sha256:16ea5caa025ef9a733b33ca99ae53d1ebe576d04eb34b4c1c4fee7ae1589615a\",\"hash\":\"sha256:28c15ee654954265e18884cd181ea9a0464bf69086bd146ea544d8114a266db2\"}\n","iterations":0,"costUsd":"0.002047","extraction":{"header":{"invoiceNumber":{"value":"PRG1502112","quote":"Rechnungsnummer PRG1502112","page":1},"issueDate":{"value":"24.04.2015","quote":"Rechnungsdatum 24.04.2015","page":1},"dueDate":{"value":"","quote":"","page":1},"buyerReference":{"value":"99000000-18188-18","quote":"Leitweg-ID / Käuferreferenz 99000000-18188-18","page":1},"orderReference":{"value":"","quote":"","page":1},"currency":{"value":"EUR","quote":"Währung EUR","page":1},"paymentTerms":{"value":"14 Tage\n","quote":"Zahlungsbedingungen\n14 Tage","page":1}},"seller":{"name":{"value":"Elbtal Medienservice UG","quote":"Elbtal Medienservice UG","page":1},"street":{"value":"Hafenstraße 12","quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Lerchenau","quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE","page":1},"postcode":{"value":"12345","quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE","page":1},"country":{"value":"DE","quote":"Elbtal Medienservice UG · Hafenstraße 12 · 12345 Lerchenau · DE","page":1},"electronicAddress":{"value":"seller@email.de","quote":"Elektronische Adresse: seller@email.de (Schema:\nEM)","page":1},"sellerId":{"value":"","quote":"","page":1},"legalRegistrationId":{"value":"HRB 123456","quote":"Handelsregister: HRB 123456","page":1},"vatId":{"value":"DE123456789","quote":"USt-IdNr.: DE123456789","page":1},"taxNumber":{"value":"","quote":"","page":1},"contactName":{"value":"Murat Yilmaz","quote":"Ansprechpartner: Murat Yilmaz","page":1},"contactPhone":{"value":"+49 123456789","quote":"Telefon: +49 123456789","page":1},"contactEmail":{"value":"xxx@schulung.de","quote":"E-Mail: xxx@schulung.de","page":1}},"buyer":{"name":{"value":"Zentrale Vergabestelle Eichstedt","quote":"Zentrale Vergabestelle Eichstedt","page":1},"street":{"value":"Bahnhofstraße 5a","quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE","page":1},"addressLine2":{"value":"","quote":"","page":1},"city":{"value":"Ahornhain","quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE","page":1},"postcode":{"value":"12345","quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE","page":1},"country":{"value":"DE","quote":"Zentrale Vergabestelle Eichstedt\nBahnhofstraße 5a\n12345 Ahornhain\nDE","page":1},"electronicAddress":{"value":"buyer@info.de","quote":"Elektronische Adresse: buyer@info.de (Schema: EM)","page":1}},"payment":{"iban":{"value":"DE79000000001234567890","quote":"IBAN: DE79000000001234567890","page":1},"accountName":{"value":"","quote":"","page":1}},"vatExemptions":[],"lines":[{"rowQuote":"1 Seminar 5 Stück 1.340 € 19 % (S) 6.700,00 €","page":1,"lineId":"1","name":"Seminar","quantity":"5","unit":"Stück","netPrice":"1.340 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"6.700,00 €"},{"rowQuote":"2 Raumkosten 5 Stunde 300 € 19 % (S) 1.500,00 €","page":1,"lineId":"2","name":"Raumkosten","quantity":"5","unit":"Stunde","netPrice":"300 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"1.500,00 €"},{"rowQuote":"3 Reisekostenpauschale 5 Stück 90 € 19 % (S) 450,00 €","page":1,"lineId":"3","name":"Reisekostenpauschale","quantity":"5","unit":"Stück","netPrice":"90 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"450,00 €"},{"rowQuote":"4 Seminarunterlagen 11 Stunde 20 € 19 % (S) 220,00 €","page":1,"lineId":"4","name":"Seminarunterlagen","quantity":"11","unit":"Stunde","netPrice":"20 €","priceBaseQuantity":null,"vatCategory":"(S)","vatRate":"19 %","lineAmount":"220,00 €"}],"documentTotals":{"lineTotal":{"value":"8.870,00 €","quote":"Summe netto 8.870,00 €","page":1},"taxTotal":{"value":"1.685,30 €","quote":"Ust 19 % (S) auf 8.870,00 € 1.685,30 €\nUmsatzsteuer 1.685,30 €","page":1},"grandTotal":{"value":"10.555,30 €","quote":"Rechnungsbetrag 10.555,30 €","page":1}}},"models":{"extract":"nvidia/nemotron-3-super-120b-a12b","repair":"nvidia/Nemotron-3-Ultra-550b-a55b"}}}}]} diff --git a/apps/web/public/gallery/unit-word.pdf b/apps/web/public/gallery/unit-word.pdf new file mode 100644 index 0000000..cf272f6 Binary files /dev/null and b/apps/web/public/gallery/unit-word.pdf differ diff --git a/apps/web/scripts/build-gallery.ts b/apps/web/scripts/build-gallery.ts new file mode 100644 index 0000000..35c3b74 --- /dev/null +++ b/apps/web/scripts/build-gallery.ts @@ -0,0 +1,108 @@ +// UI-06: records real runs on synthetic corpus PDFs as replayable event streams. +// The gallery works without an API key or budget because it replays these recordings. +// pnpm --filter @verdict/web gallery:build (needs NEBIUS_API_KEY and a running verifier) +import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { TokenFactoryClient, type UserInput } from "@verdict/core"; +import { executeRun } from "../lib/run-core.ts"; +import type { RunEvent, RunResultView } from "../lib/run-protocol.ts"; + +const ROOT = resolve(import.meta.dirname, "../../.."); +const CORPUS = join(ROOT, "eval/corpus/generated"); +const OUT = resolve(import.meta.dirname, "../public/gallery"); + +interface GalleryCase { + readonly id: string; + readonly corpus: string; + readonly title: string; + readonly story: string; + /** A follow-up run that answers the questions of a NEEDS_INPUT run (synthetic values). */ + readonly answer?: readonly UserInput[]; +} + +const CASES: readonly GalleryCase[] = [ + { id: "clean", corpus: "clean/01.01a-classic", title: "A clean invoice", story: "Every field quoted from the page, totals computed, KoSIT accepts it." }, + { id: "seven-lines", corpus: "clean/01.06a-classic", title: "Seven line items", story: "Seven line items. Nemotron Ultra repairs three fields behind the patch guard; every amount is computed from the lines." }, + { id: "noisy-layout", corpus: "noisy/01.12a-noisy-a", title: "Unusual layout", story: "Plain number format, ISO dates and shuffled blocks still end in a valid e-invoice." }, + { id: "unit-word", corpus: "mutated/01.05a-unit-as-german-word", title: "Units printed as words", story: "\"Stück\" is not a unit code. KoSIT reports it as an error yet still accepts the invoice; Verdict normalizes it to H87." }, + { id: "total-off", corpus: "mutated/03.06a-grand-total-off", title: "Printed total is wrong", story: "The document adds up to 1.00 less than it claims. Verdict trusts the lines, computes the total and flags the difference." }, + { + id: "missing-leitweg", + corpus: "mutated/01.01a-missing-buyer-reference", + title: "Leitweg-ID missing", + story: "The buyer reference is not printed. Verdict asks for it instead of inventing one.", + answer: [{ path: "buyerReference", value: "04011000-12345-03" }], + }, + { + id: "missing-iban", + corpus: "mutated/01.08a-missing-iban", + title: "Bank details missing", + story: "Payment by transfer, but no IBAN on the page. Verdict stops and asks.", + // Public test IBAN, not a real account. + answer: [{ path: "payment.iban", value: "DE02 1203 0000 0000 2020 51" }], + }, +]; + +const validate = async (xml: string) => { + const res = await fetch(`${process.env.VERIFIER_URL || "http://localhost:8081"}/v1/validate/xrechnung`, { + method: "POST", + headers: { "X-Verifier-Secret": process.env.VERIFIER_SECRET ?? "", "Content-Type": "application/xml" }, + body: xml, + }); + if (!res.ok) throw new Error(`verifier ${res.status}`); + return (await res.json()) as Awaited[0]["validate"]>>; +}; + +async function record(deps: Parameters[0], pdf: Uint8Array, resume?: Parameters[3]) { + const events: { at: number; event: RunEvent }[] = []; + const t0 = performance.now(); + await executeRun(deps, pdf, (event) => events.push({ at: Math.round(performance.now() - t0), event }), resume); + const result = events.find((e) => e.event.type === "result")?.event as { result: RunResultView } | undefined; + return { events, result: result?.result }; +} + +const deps = { + client: TokenFactoryClient.fromEnv(), + models: { + extract: process.env.MODEL_EXTRACT || "nvidia/nemotron-3-super-120b-a12b", + repair: process.env.MODEL_REPAIR || "nvidia/Nemotron-3-Ultra-550b-a55b", + }, + validate, +}; + +// --only re-records one case and keeps the others. +const only = process.argv.includes("--only") ? process.argv[process.argv.indexOf("--only") + 1] : undefined; +mkdirSync(OUT, { recursive: true }); +type IndexRow = { id: string; title: string; story: string; state: string; followUpState?: string }; +const previous: IndexRow[] = only + ? (JSON.parse(readFileSync(join(OUT, "index.json"), "utf8")) as { cases: IndexRow[] }).cases + : []; +const index: IndexRow[] = []; +for (const c of CASES) { + if (only && c.id !== only) { + const kept = previous.find((p) => p.id === c.id); + if (kept) index.push(kept); + continue; + } + const pdf = readFileSync(join(CORPUS, `${c.corpus}.pdf`)); + copyFileSync(join(CORPUS, `${c.corpus}.pdf`), join(OUT, `${c.id}.pdf`)); + const first = await record(deps, pdf); + let followUp: Awaited> | undefined; + if (c.answer && first.result?.state === "NEEDS_INPUT") { + followUp = await record(deps, pdf, { extraction: first.result.extraction, userInputs: c.answer }); + } + writeFileSync( + join(OUT, `${c.id}.json`), + JSON.stringify({ id: c.id, title: c.title, story: c.story, pdf: `${c.id}.pdf`, synthetic: true, run: first.events, ...(followUp ? { answer: c.answer, followUp: followUp.events } : {}) }) + "\n", + ); + index.push({ + id: c.id, + title: c.title, + story: c.story, + state: first.result?.state ?? "ERROR", + ...(followUp ? { followUpState: followUp.result?.state ?? "ERROR" } : {}), + }); + console.log(`${c.id}: ${first.result?.state ?? "ERROR"}${followUp ? ` -> ${followUp.result?.state}` : ""} (${first.result?.costUsd ?? "?"} USD)`); +} +writeFileSync(join(OUT, "index.json"), JSON.stringify({ generatedAt: new Date().toISOString().slice(0, 10), cases: index }, null, 2) + "\n"); +console.log(`spent: ${deps.client.ledger.spentToday().toFixed(4)} USD`); diff --git a/apps/web/scripts/copy-pdf-worker.mjs b/apps/web/scripts/copy-pdf-worker.mjs new file mode 100644 index 0000000..a9d731a --- /dev/null +++ b/apps/web/scripts/copy-pdf-worker.mjs @@ -0,0 +1,9 @@ +// The browser loads the pdf.js worker as a static file; copy it from the installed package. +import { copyFileSync, mkdirSync } from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; + +const require = createRequire(import.meta.url); +const pkg = dirname(require.resolve("pdfjs-dist/package.json")); +mkdirSync(new URL("../public/", import.meta.url), { recursive: true }); +copyFileSync(join(pkg, "build/pdf.worker.min.mjs"), new URL("../public/pdf.worker.min.mjs", import.meta.url)); diff --git a/apps/web/src/index.ts b/apps/web/src/index.ts deleted file mode 100644 index d322f80..0000000 --- a/apps/web/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Placeholder. The Next.js app is built in phase 4 (UI-01 – UI-07). -export {}; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 008c945..763fc36 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1 +1,13 @@ -{ "extends": "../../tsconfig.base.json", "include": ["src"] } +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "preserve", + "lib": ["dom", "dom.iterable", "es2023"], + "types": ["node", "react", "react-dom"], + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] } + }, + "include": ["next-env.d.ts", "app", "lib", "scripts", "components", ".next/types/**/*.ts"] +} diff --git a/eval/src/pipeline.ts b/eval/src/pipeline.ts index 196fd4c..78bad64 100644 --- a/eval/src/pipeline.ts +++ b/eval/src/pipeline.ts @@ -5,13 +5,13 @@ import { assembleInvoice, buildCii, deriveAmounts, + extractPageTexts, extractRaw, InvoiceInput, runInvoice, TokenFactoryClient, type InvoiceInput as InvoiceInputT, } from "@verdict/core"; -import { extractPageTexts } from "./render/extract-text.ts"; import { CORPUS_DIR, type ManifestEntry } from "./corpus/manifest.ts"; import { validateXRechnung, type VerifierResult } from "./verifier-client.ts"; diff --git a/eval/src/render/render-invoice.test.ts b/eval/src/render/render-invoice.test.ts index 1cc7c90..10ea298 100644 --- a/eval/src/render/render-invoice.test.ts +++ b/eval/src/render/render-invoice.test.ts @@ -2,7 +2,7 @@ // deleted fields leave no label behind. import { deriveAmounts, type InvoiceInput } from "@verdict/core"; import { describe, expect, it } from "vitest"; -import { extractPageTexts } from "./extract-text.ts"; +import { extractPageTexts } from "@verdict/core"; import { formatAmount, labelsFor, diff --git a/packages/core/package.json b/packages/core/package.json index 716213f..a3e27e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -3,7 +3,8 @@ "private": true, "type": "module", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./rule-fields": "./src/agent/rule-fields.ts" }, "scripts": { "typecheck": "tsc -p tsconfig.json", @@ -15,6 +16,7 @@ "@xmldom/xmldom": "^0.9.12", "canonicalize": "^5.1.0", "decimal.js": "^10.6.0", + "pdfjs-dist": "^6.3.289", "xpath": "^0.0.34", "zod": "^4.6.5" } diff --git a/packages/core/src/agent/rule-fields.ts b/packages/core/src/agent/rule-fields.ts new file mode 100644 index 0000000..39f864a --- /dev/null +++ b/packages/core/src/agent/rule-fields.ts @@ -0,0 +1,20 @@ +// Browser-safe: no Node imports, so the web client can import it via "@verdict/core/rule-fields". + +/** + * Validator rules whose violation means a fact is simply not on the document, mapped to the + * model field the user can supply. Used to turn an unresolved rejection into a precise question. + */ +export const RULE_FIELDS: Readonly> = { + "BR-DE-1": "payment.iban", + "BR-DE-15": "buyerReference", + "BR-DE-3": "seller.address.city", + "BR-DE-4": "seller.address.postcode", + "BR-DE-6": "seller.contact.phone", + "BR-DE-7": "seller.contact.email", + "BR-DE-8": "buyer.address.city", + "BR-DE-9": "buyer.address.postcode", + "BR-62": "seller.electronicAddress.value", + "BR-63": "buyer.electronicAddress.value", + "BR-CO-26": "seller.vatId", + "BR-S-02": "seller.vatId", +}; diff --git a/packages/core/src/agent/run-invoice.ts b/packages/core/src/agent/run-invoice.ts index d3bd653..2b07575 100644 --- a/packages/core/src/agent/run-invoice.ts +++ b/packages/core/src/agent/run-invoice.ts @@ -1,14 +1,15 @@ import { Decimal } from "decimal.js"; -import { AuditLog, sha256, type AuditActor } from "../audit/audit-log.ts"; +import { AuditLog, sha256, type AuditActor, type AuditEvent } from "../audit/audit-log.ts"; import { buildCii } from "../cii/build.ts"; import { deriveAmounts } from "../derive/derive.ts"; -import { assembleInvoice, btOf, type MissingFact, type Provenance } from "../extract/assemble.ts"; +import { assembleInvoice, btOf, type Assembled, type MissingFact, type Provenance, type RejectedFact } from "../extract/assemble.ts"; import { extractRaw, pagesToPrompt } from "../extract/extract.ts"; -import { EXTRACTION_SYSTEM_PROMPT } from "../extract/schema.ts"; +import { EXTRACTION_SYSTEM_PROMPT, type RawExtraction } from "../extract/schema.ts"; import type { ChatResult, TokenFactoryClient } from "../llm/token-factory-client.ts"; import { InvoiceInput, type InvoiceInput as InvoiceInputT } from "../model/invoice.ts"; import { applyPatches, guardPatches, type AcceptedPatch, type RejectedPatch } from "../repair/patch-guard.ts"; import { planRepair, REPAIR_SYSTEM_PROMPT, type RepairIssue } from "../repair/repair.ts"; +import { RULE_FIELDS } from "./rule-fields.ts"; export const CORE_VERSION = "0.1.0"; @@ -38,6 +39,10 @@ export interface RunOptions { readonly userInputs?: readonly UserInput[]; readonly now?: () => Date; readonly onStep?: (step: string, detail: Record) => void; + /** Every audit event as it is appended (drives the live run sheet in the UI). */ + readonly onAudit?: (event: AuditEvent) => void; + /** Resume with a previous extraction (e.g. after user input) instead of calling the model again. */ + readonly extraction?: RawExtraction; } export interface RunResult { @@ -54,11 +59,19 @@ export interface RunResult { readonly patchesRejected: readonly RejectedPatch[]; readonly provenance: ReadonlyMap; readonly fieldsWithoutProvenance: readonly string[]; + /** Facts the guards refused (evidence, shape, role, normalization), with the printed value. */ + readonly rejectedFacts: readonly RejectedFact[]; + /** The raw extraction, so a NEEDS_INPUT run can resume without another model call. */ + readonly extraction: RawExtraction; + readonly documentTotals: Assembled["documentTotals"]; readonly audit: AuditLog; readonly costUsd: string; } const core: AuditActor = { kind: "core", version: CORE_VERSION }; +export { RULE_FIELDS } from "./rule-fields.ts"; + + /** * The agent loop (docs/SRS.md §4) for a document with a text layer: @@ -66,7 +79,7 @@ const core: AuditActor = { kind: "core", version: CORE_VERSION }; * with up to 3 REPAIR_PLAN/PATCH_GUARD rounds, ending in OUTPUT or NEEDS_INPUT. */ export async function runInvoice(opts: RunOptions): Promise { - const audit = new AuditLog(opts.now); + const audit = new AuditLog(opts.now, opts.onAudit); const maxIterations = opts.maxIterations ?? 3; const step = (name: string, detail: Record) => opts.onStep?.(name, detail); let cost = new Decimal(0); @@ -89,8 +102,11 @@ export async function runInvoice(opts: RunOptions): Promise { audit.append({ type: "INGEST", actor: core, inputHash: opts.uploadHash, data: { pages: opts.pages.length } }); step("INGEST", { pages: opts.pages.length }); - const extraction = await extractRaw(opts.client, opts.models.extract, opts.pages); + const extraction = opts.extraction + ? { raw: opts.extraction, calls: [] as ChatResult[] } + : await extractRaw(opts.client, opts.models.extract, opts.pages); for (const call of extraction.calls) modelEvent("EXTRACT", call, EXTRACTION_SYSTEM_PROMPT + pagesToPrompt(opts.pages)); + if (opts.extraction) audit.append({ type: "EXTRACT", actor: core, data: { reused: true } }); step("EXTRACT", { calls: extraction.calls.length }); const assembled = assembleInvoice(extraction.raw, opts.pages); @@ -104,8 +120,14 @@ export async function runInvoice(opts: RunOptions): Promise { const provenance = new Map(assembled.provenance); let partial: unknown = assembled.partial; for (const input of opts.userInputs ?? []) { - partial = setPath(partial, input.path, input.value); + const value = input.path === "payment.iban" ? input.value.replace(/\s/g, "").toUpperCase() : input.value; + partial = setPath(partial, input.path, value); provenance.set(input.path, { kind: "user", enteredAt: (opts.now ?? (() => new Date()))().toISOString() }); + if (input.path === "payment.iban") { + // An IBAN means payment by SEPA credit transfer, the only means in the MVP. + partial = setPath(partial, "payment.meansCode", "58"); + provenance.set("payment.meansCode", { kind: "derived", rule: "credit-transfer-from-iban", inputs: ["payment.iban"] }); + } } if (opts.userInputs?.length) { audit.append({ type: "USER_INPUT", actor: { kind: "user" }, data: { fields: opts.userInputs.map((u) => btOf(u.path)) } }); @@ -216,7 +238,15 @@ export async function runInvoice(opts: RunOptions): Promise { return true; } - function needsInput(missing: MissingFact[], issues: RepairIssue[]): RunResult { + function needsInput(missingIn: MissingFact[], issues: RepairIssue[]): RunResult { + // Validator rejections that only mean "fact not printed" become precise questions too. + const missing = [...missingIn]; + for (const issue of issues) { + const path = RULE_FIELDS[issue.id]; + if (path && !missing.some((m) => m.path === path)) { + missing.push({ path, bt: btOf(path), reason: `required by ${issue.id} but not on the document` }); + } + } audit.append({ type: "NEEDS_INPUT", actor: core, @@ -244,6 +274,9 @@ export async function runInvoice(opts: RunOptions): Promise { patchesRejected: rejectedPatches, provenance, fieldsWithoutProvenance: r.withoutProvenance, + rejectedFacts: assembled.rejected, + extraction: extraction.raw, + documentTotals: assembled.documentTotals, audit, costUsd: cost.toFixed(6), }; diff --git a/packages/core/src/audit/audit-log.ts b/packages/core/src/audit/audit-log.ts index 0fe664d..bd87894 100644 --- a/packages/core/src/audit/audit-log.ts +++ b/packages/core/src/audit/audit-log.ts @@ -53,9 +53,11 @@ function eventHash(event: Omit): string { export class AuditLog { readonly #events: AuditEvent[] = []; readonly #now: () => Date; + readonly #onAppend: ((event: AuditEvent) => void) | undefined; - constructor(now: () => Date = () => new Date()) { + constructor(now: () => Date = () => new Date(), onAppend?: (event: AuditEvent) => void) { this.#now = now; + this.#onAppend = onAppend; } append(entry: { @@ -78,6 +80,7 @@ export class AuditLog { }; const event: AuditEvent = { ...base, hash: eventHash(base) }; this.#events.push(event); + this.#onAppend?.(event); return event; } diff --git a/packages/core/src/evidence/evidence-check.test.ts b/packages/core/src/evidence/evidence-check.test.ts index 262207b..0f615f5 100644 --- a/packages/core/src/evidence/evidence-check.test.ts +++ b/packages/core/src/evidence/evidence-check.test.ts @@ -16,6 +16,10 @@ describe("checkEvidence", () => { expect(checkEvidence(pages, { page: 2, quote: "IBAN: DE7900000000123456789 0" }).ok).toBe(true); }); + it("treats a literal \\n in a quote as a line break", () => { + expect(checkEvidence(pages, { page: 1, quote: "Rechnung Nr. R-2026-0042\\nDatum: 01.10.2026" }).ok).toBe(true); + }); + it("rejects quotes from the wrong page", () => { expect(checkEvidence(pages, { page: 1, quote: "Gesamtbetrag 1.190,00 EUR" })).toEqual({ ok: false, reason: "quote-not-on-page" }); }); diff --git a/packages/core/src/evidence/evidence-check.ts b/packages/core/src/evidence/evidence-check.ts index 5b52946..07f2c73 100644 --- a/packages/core/src/evidence/evidence-check.ts +++ b/packages/core/src/evidence/evidence-check.ts @@ -16,6 +16,9 @@ export type EvidenceVerdict = export function canonical(s: string): string { return s + // The extraction prompt asks for line breaks as the two characters "\n"; treat them as + // whitespace, like a real line break. + .replace(/\\n/g, " ") .normalize("NFKC") .replace(/[‐-―−]/g, "-") .replace(/[   ]/g, " ") diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5efba79..5e2d0e7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -36,3 +36,4 @@ export * from "./repair/patch-guard.ts"; export * from "./repair/repair.ts"; export * from "./audit/audit-log.ts"; export * from "./agent/run-invoice.ts"; +export { extractPageTexts } from "./pdf/extract-text.ts"; diff --git a/eval/src/render/extract-text.ts b/packages/core/src/pdf/extract-text.ts similarity index 58% rename from eval/src/render/extract-text.ts rename to packages/core/src/pdf/extract-text.ts index 857bc61..0db421f 100644 --- a/eval/src/render/extract-text.ts +++ b/packages/core/src/pdf/extract-text.ts @@ -1,13 +1,31 @@ // Text-layer extraction via pdf.js (legacy Node build). Items are emitted in content-stream // order; items on the same baseline are joined with a space, a new baseline starts a new line. +import { existsSync } from "node:fs"; import { createRequire } from "node:module"; import { dirname, join, sep } from "node:path"; import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; -const require = createRequire(import.meta.url); -// In Node, pdf.js reads this with fs.readFile, so it must be a plain path with a trailing separator. -const STANDARD_FONTS = join(dirname(require.resolve("pdfjs-dist/package.json")), "standard_fonts") + sep; +/** + * pdf.js reads its standard font data with fs.readFile, so it needs a plain path with a trailing + * separator. Inside a bundled server (Next.js) require.resolve returns a module id instead of a + * path, so resolve lazily and fall back to node_modules under the working directory. + */ +let standardFonts: string | null | undefined; +function standardFontsPath(): string | undefined { + if (standardFonts !== undefined) return standardFonts ?? undefined; + const candidates: string[] = []; + try { + const resolved: unknown = createRequire(import.meta.url).resolve("pdfjs-dist/package.json"); + if (typeof resolved === "string") candidates.push(join(dirname(resolved), "standard_fonts")); + } catch { + // fall through to the cwd-based candidates + } + candidates.push(join(process.cwd(), "node_modules", "pdfjs-dist", "standard_fonts")); + const found = candidates.find((c) => existsSync(c)); + standardFonts = found ? found + sep : null; + return standardFonts ?? undefined; +} interface Item { readonly str: string; @@ -21,7 +39,7 @@ const isItem = (x: unknown): x is Item => export async function extractPageTexts(pdf: Buffer): Promise { const task = getDocument({ data: new Uint8Array(pdf), - standardFontDataUrl: STANDARD_FONTS, + ...(standardFontsPath() ? { standardFontDataUrl: standardFontsPath() } : {}), verbosity: 0, }); const doc = await task.promise; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06e8a5c..5ecf3b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,9 +19,59 @@ importers: version: 7.0.2 vitest: specifier: ^5.0.3 - version: 5.0.3(@types/node@26.6.4)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@types/node@26.6.4)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) - apps/web: {} + apps/web: + dependencies: + '@fontsource/barlow': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/barlow-condensed': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/jetbrains-mono': + specifier: ^5.3.0 + version: 5.3.0 + '@verdict/core': + specifier: workspace:* + version: link:../../packages/core + decimal.js: + specifier: ^10.6.0 + version: 10.6.0 + lucide-react: + specifier: ^1.50.0 + version: 1.50.0(@types/react@19.3.0)(react@19.3.0) + next: + specifier: ^16.3.8 + version: 16.3.8(@types/node@26.6.4)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + pdfjs-dist: + specifier: ^6.3.289 + version: 6.3.289 + react: + specifier: ^19.3.0 + version: 19.3.0 + react-dom: + specifier: ^19.3.0 + version: 19.3.0(react@19.3.0) + server-only: + specifier: ^0.0.1 + version: 0.0.1 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.3.3 + version: 4.3.3 + '@types/react': + specifier: ^19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: ^19.3.0 + version: 19.3.0(@types/react@19.3.0) + tailwindcss: + specifier: ^4.3.3 + version: 4.3.3 + tsx: + specifier: ^4.23.15 + version: 4.23.15 eval: dependencies: @@ -53,6 +103,9 @@ importers: decimal.js: specifier: ^10.6.0 version: 10.6.0 + pdfjs-dist: + specifier: ^6.3.289 + version: 6.3.289 xpath: specifier: ^0.0.34 version: 0.0.34 @@ -62,6 +115,13 @@ importers: packages: + '@alloc/quick-lru@5.3.0': + resolution: {integrity: sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==} + engines: {node: '>=10'} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -218,6 +278,183 @@ packages: cpu: [x64] os: [win32] + '@fontsource/barlow-condensed@5.3.0': + resolution: {integrity: sha512-RA7MmIx0v3eQ6Gcf74qLVLOwLB0GpPOzx+eU9iG/AmCnSJGcIWYeU8y6CEMGZ210MGwGg5CZhzLkkvyZdTTk1g==} + + '@fontsource/barlow@5.3.0': + resolution: {integrity: sha512-tGR0y6QObRtKv71MOth44222+mdNFM/ZWFWDV1VLzNbG4oK9PNRlyJzmkck4vHAi9ibHBXgt4SlY51amFP55CA==} + + '@fontsource/jetbrains-mono@5.3.0': + resolution: {integrity: sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -303,6 +540,61 @@ packages: resolution: {integrity: sha512-V+qptzsGnPMFxEbhwc6eDNMe5eDrfiBIp8qGpuRjAVIX4Kn2zxvwQ1Xco6Fwe+3tYIe3iLW8LM9D+SvjyJhqbQ==} engines: {node: '>= 10'} + '@next/env@16.3.8': + resolution: {integrity: sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw==} + + '@next/swc-darwin-arm64@16.3.8': + resolution: {integrity: sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.8': + resolution: {integrity: sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.8': + resolution: {integrity: sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.3.8': + resolution: {integrity: sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.3.8': + resolution: {integrity: sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.3.8': + resolution: {integrity: sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.3.8': + resolution: {integrity: sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.8': + resolution: {integrity: sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} @@ -416,6 +708,98 @@ packages: '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -431,6 +815,14 @@ packages: '@types/pdfkit@0.17.6': resolution: {integrity: sha512-tIwzxk2uWKp0Cq9JIluQXJid77lYhF52EsIOwhsMF4iWLA6YneoBR1xVKYYdAysHuepUB0OX4tdwMiUDdGKmig==} + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} @@ -580,9 +972,17 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.11.27: + resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==} + engines: {node: '>=6.0.0'} + hasBin: true + brotli@1.3.3: resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + caniuse-lite@1.0.30001814: + resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==} + canonicalize@5.1.0: resolution: {integrity: sha512-bab9+WefZljqQ8jT0qRwVmTyzAUU63K97QIQD3agynIBrb6sa5M2eAy8Hv8K3p4V9+ONVYZKtGYl4Dg+/5Gumw==} engines: {node: '>=22'} @@ -592,10 +992,16 @@ packages: resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} engines: {node: '>=18'} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -606,6 +1012,10 @@ packages: dfa@1.2.0: resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + enhanced-resolve@5.26.0: + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + engines: {node: '>=10.13.0'} + es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} @@ -644,36 +1054,80 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-arm64@1.33.0: resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-darwin-x64@1.33.0: resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-freebsd-x64@1.33.0: resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} engines: {node: '>= 12.0.0'} @@ -681,6 +1135,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} engines: {node: '>= 12.0.0'} @@ -688,6 +1149,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} engines: {node: '>= 12.0.0'} @@ -695,6 +1163,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-musl@1.33.0: resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} engines: {node: '>= 12.0.0'} @@ -702,18 +1177,34 @@ packages: os: [linux] libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss-win32-x64-msvc@1.33.0: resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} @@ -721,6 +1212,18 @@ packages: linebreak@1.1.0: resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lucide-react@1.50.0: + resolution: {integrity: sha512-RqHPQtKX6S9IE1xOL4oTHHsVQbcwJWTnmlxI6AdI9IpOwKiy7q+COPCQyMepI/Qu1x3h7sunpyEMvqtgFbgNiQ==} + peerDependencies: + '@types/react': '*' + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.2: resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} @@ -729,6 +1232,27 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + next@16.3.8: + resolution: {integrity: sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + obug@2.2.1: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} @@ -753,10 +1277,23 @@ packages: png-js@2.0.0: resolution: {integrity: sha512-GdzJuUMc6ZSpxFJWVxtOH1bzYHym+TOnveqUjb+VJIbZWbZzyiRGFiKhbiielfpYbgMlhHVhsJ0FTazfuRFkMA==} + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + peerDependencies: + react: ^19.3.0 + + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + engines: {node: '>=0.10.0'} + restructure@3.0.2: resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} @@ -765,6 +1302,26 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + source-map-js@1.2.2: resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} @@ -772,6 +1329,26 @@ packages: std-env@4.3.0: resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -907,6 +1484,13 @@ packages: snapshots: + '@alloc/quick-lru@5.3.0': {} + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.28.2': optional: true @@ -985,6 +1569,129 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true + '@fontsource/barlow-condensed@5.3.0': {} + + '@fontsource/barlow@5.3.0': {} + + '@fontsource/jetbrains-mono@5.3.0': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.4 + optional: true + + '@img/sharp-darwin-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + + '@img/sharp-linux-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.4 + optional: true + + '@img/sharp-linux-arm@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.4 + optional: true + + '@img/sharp-linux-ppc64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.4 + optional: true + + '@img/sharp-linux-riscv64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.4 + optional: true + + '@img/sharp-linux-s390x@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.4 + optional: true + + '@img/sharp-linux-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + optional: true + + '@img/sharp-wasm32@0.35.5': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': + optional: true + + '@img/sharp-win32-ia32@0.35.5': + optional: true + + '@img/sharp-win32-x64@0.35.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.6.0': {} @@ -1042,6 +1749,32 @@ snapshots: '@napi-rs/canvas-win32-x64-msvc': 1.0.10 optional: true + '@next/env@16.3.8': {} + + '@next/swc-darwin-arm64@16.3.8': + optional: true + + '@next/swc-darwin-x64@16.3.8': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.8': + optional: true + + '@next/swc-linux-arm64-musl@16.3.8': + optional: true + + '@next/swc-linux-x64-gnu@16.3.8': + optional: true + + '@next/swc-linux-x64-musl@16.3.8': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.8': + optional: true + + '@next/swc-win32-x64-msvc@16.3.8': + optional: true + '@noble/ciphers@1.3.0': {} '@noble/hashes@1.8.0': {} @@ -1099,6 +1832,75 @@ snapshots: dependencies: tslib: 2.8.1 + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.26.0 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.2 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.3.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.28 + tailwindcss: 4.3.3 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -1116,6 +1918,14 @@ snapshots: dependencies: '@types/node': 26.6.4 + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -1176,14 +1986,14 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15))': + '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.3 estree-walker: 3.0.3 magic-string: 1.4.2 optionalDependencies: - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15) '@vitest/spy@5.0.3': {} @@ -1195,22 +2005,35 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.11.27: {} + brotli@1.3.3: dependencies: base64-js: 1.5.1 + caniuse-lite@1.0.30001814: {} + canonicalize@5.1.0: {} chai@6.3.0: {} + client-only@0.0.1: {} + clone@2.1.2: {} + csstype@3.2.3: {} + decimal.js@10.6.0: {} detect-libc@2.1.2: {} dfa@1.2.0: {} + enhanced-resolve@5.26.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + es-module-lexer@2.3.2: {} esbuild@0.28.2: @@ -1271,39 +2094,92 @@ snapshots: fsevents@2.3.3: optional: true + graceful-fs@4.2.11: {} + + jiti@2.7.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + lightningcss-android-arm64@1.33.0: optional: true + lightningcss-darwin-arm64@1.32.0: + optional: true + lightningcss-darwin-arm64@1.33.0: optional: true + lightningcss-darwin-x64@1.32.0: + optional: true + lightningcss-darwin-x64@1.33.0: optional: true + lightningcss-freebsd-x64@1.32.0: + optional: true + lightningcss-freebsd-x64@1.33.0: optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: optional: true + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + lightningcss-linux-arm64-gnu@1.33.0: optional: true + lightningcss-linux-arm64-musl@1.32.0: + optional: true + lightningcss-linux-arm64-musl@1.33.0: optional: true + lightningcss-linux-x64-gnu@1.32.0: + optional: true + lightningcss-linux-x64-gnu@1.33.0: optional: true + lightningcss-linux-x64-musl@1.32.0: + optional: true + lightningcss-linux-x64-musl@1.33.0: optional: true + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + lightningcss-win32-arm64-msvc@1.33.0: optional: true + lightningcss-win32-x64-msvc@1.32.0: + optional: true + lightningcss-win32-x64-msvc@1.33.0: optional: true + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: dependencies: detect-libc: 2.1.2 @@ -1325,12 +2201,47 @@ snapshots: base64-js: 0.0.8 unicode-trie: 2.0.0 + lucide-react@1.50.0(@types/react@19.3.0)(react@19.3.0): + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magic-string@1.4.2: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 nanoid@3.3.19: {} + next@16.3.8(@types/node@26.6.4)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + '@next/env': 16.3.8 + '@swc/helpers': 0.5.23 + baseline-browser-mapping: 2.11.27 + caniuse-lite: 1.0.30001814 + postcss: 8.5.23 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + styled-jsx: 5.1.6(react@19.3.0) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.8 + '@next/swc-darwin-x64': 16.3.8 + '@next/swc-linux-arm64-gnu': 16.3.8 + '@next/swc-linux-arm64-musl': 16.3.8 + '@next/swc-linux-x64-gnu': 16.3.8 + '@next/swc-linux-x64-musl': 16.3.8 + '@next/swc-win32-arm64-msvc': 16.3.8 + '@next/swc-win32-x64-msvc': 16.3.8 + sharp: 0.35.5(@types/node@26.6.4) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + obug@2.2.1: {} pako@0.2.9: {} @@ -1356,12 +2267,25 @@ snapshots: dependencies: fflate: 0.8.3 + postcss@8.5.23: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 + postcss@8.5.28: dependencies: nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.2 + react-dom@19.3.0(react@19.3.0): + dependencies: + react: 19.3.0 + scheduler: 0.28.0 + + react@19.3.0: {} + restructure@3.0.2: {} rolldown@1.2.12: @@ -1385,10 +2309,60 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.12 '@rolldown/binding-win32-x64-msvc': 1.2.12 + scheduler@0.28.0: {} + + semver@7.8.5: + optional: true + + server-only@0.0.1: {} + + sharp@0.35.5(@types/node@26.6.4): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 26.6.4 + optional: true + source-map-js@1.2.2: {} std-env@4.3.0: {} + styled-jsx@5.1.6(react@19.3.0): + dependencies: + client-only: 0.0.1 + react: 19.3.0 + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + tiny-inflate@1.0.3: {} tinybench@6.2.0: {} @@ -1443,7 +2417,7 @@ snapshots: pako: 0.2.9 tiny-inflate: 1.0.3 - vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15): + vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -1454,12 +2428,13 @@ snapshots: '@types/node': 26.6.4 esbuild: 0.28.2 fsevents: 2.3.3 + jiti: 2.7.0 tsx: 4.23.15 - vitest@5.0.3(@types/node@26.6.4)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)): + vitest@5.0.3(@types/node@26.6.4)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -1470,7 +2445,7 @@ snapshots: tinybench: 6.2.0 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15) why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 26.6.4