M0: arc42-Konzeptdokument, Gherkin-Anforderungen und OpenAPI-Vertrag
- doc/architecture.md: arc42-Detaildokument mit Mermaid-Diagrammen (Bausteinsicht, Klassendiagramm, Statusmodell, 5 Sequenzdiagramme), Speicherkonzept, DEC-01..19-Entscheidungen mit Trade-offs - doc/requirements/: 7 Features als Gherkin-Szenarien (TDD-Grundlage) mit Traceability-Tabellen Szenario <-> Stdlib-Testname - doc/openapi.yaml: API-Vertrag fuer alle 9 Endpunkte (Quelle der Wahrheit) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# Feature: Dokumenten-Upload über die API
|
||||
|
||||
Als API-Client möchte ich Dokumente (PDF, JPG, PNG, HEIC) hochladen,
|
||||
damit sie automatisch verarbeitet, durchsuchbar gemacht und abgelegt werden.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Dokumenten-Upload über die API
|
||||
|
||||
Szenario: Erfolgreicher PDF-Upload
|
||||
Angenommen ein gültiger API-Token
|
||||
Wenn ein PDF per POST /v1/documents (multipart, Feld "file") hochgeladen wird
|
||||
Dann antwortet das System mit 202 und einer neuen Dokument-UUID
|
||||
Und das Dokument hat den Status "received"
|
||||
Und die Datei liegt unter data/staging/{uuid}.pdf
|
||||
Und ein Event "DocumentReceived" mit origin "api" wurde aufgezeichnet
|
||||
|
||||
Szenario: Upload eines Bildes
|
||||
Angenommen ein gültiger API-Token
|
||||
Wenn eine JPG-, PNG- oder HEIC-Datei hochgeladen wird
|
||||
Dann antwortet das System mit 202
|
||||
Und der media_type des Dokuments entspricht dem Dateityp
|
||||
|
||||
Szenario: Duplikat-Upload wird abgelehnt
|
||||
Angenommen ein bereits importiertes Dokument mit bekanntem SHA-256-Hash
|
||||
Wenn eine byte-identische Datei erneut hochgeladen wird
|
||||
Dann antwortet das System mit 409
|
||||
Und die Antwort enthält error.code "duplicate_document" und die existing_document_id
|
||||
Und es wurde kein neues Aggregat angelegt
|
||||
|
||||
Szenario: Upload ohne Token wird abgelehnt
|
||||
Angenommen kein oder ein falscher API-Token
|
||||
Wenn ein Dokument hochgeladen wird
|
||||
Dann antwortet das System mit 401
|
||||
|
||||
Szenario: Nicht unterstützter Dateityp wird abgelehnt
|
||||
Angenommen ein gültiger API-Token
|
||||
Wenn eine Datei mit nicht unterstützter Endung (z.B. .docx) hochgeladen wird
|
||||
Dann antwortet das System mit 422 und error.code "unsupported_media_type"
|
||||
|
||||
Szenario: Upload über dem Größenlimit wird abgelehnt
|
||||
Angenommen ein konfiguriertes Upload-Limit von 100 MB
|
||||
Wenn eine größere Datei hochgeladen wird
|
||||
Dann antwortet das System mit 413
|
||||
Und es verbleibt keine .part-Datei im Staging
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Paket `internal/api`, `internal/ingest`) |
|
||||
|---|---|
|
||||
| Erfolgreicher PDF-Upload | `TestUpload_PDFReturns202AndReceived` |
|
||||
| Upload eines Bildes | `TestUpload_ImageSetsMediaType` |
|
||||
| Duplikat-Upload wird abgelehnt | `TestUpload_DuplicateReturns409` |
|
||||
| Upload ohne Token wird abgelehnt | `TestUpload_MissingTokenReturns401` |
|
||||
| Nicht unterstützter Dateityp wird abgelehnt | `TestUpload_UnsupportedTypeReturns422` |
|
||||
| Upload über dem Größenlimit wird abgelehnt | `TestUpload_TooLargeReturns413AndCleansUp` |
|
||||
@@ -0,0 +1,54 @@
|
||||
# Feature: Automatischer Import aus dem überwachten Ordner
|
||||
|
||||
Als Nutzer möchte ich Dateien einfach im Import-Ordner ablegen,
|
||||
damit sie ohne manuellen Eingriff verarbeitet werden.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Automatischer Import aus dem überwachten Ordner
|
||||
|
||||
Szenario: Neue Datei wird automatisch erkannt und verarbeitet
|
||||
Angenommen der Watcher überwacht den Import-Ordner
|
||||
Wenn eine PDF-Datei im Import-Ordner gespeichert wird
|
||||
Dann wird die Datei nach Abschluss des Schreibvorgangs in das Staging übernommen
|
||||
Und ein Event "DocumentReceived" mit origin "import_folder" wurde aufgezeichnet
|
||||
Und die Datei liegt nicht mehr im Import-Ordner
|
||||
|
||||
Szenario: Unvollständig geschriebene Datei wird nicht vorzeitig übernommen
|
||||
Angenommen eine Datei wird langsam in den Import-Ordner geschrieben
|
||||
Wenn sich Größe oder Änderungszeit innerhalb der Stabilitätsfrist noch ändern
|
||||
Dann wird die Datei noch nicht übernommen
|
||||
Und erst nach unveränderter Stabilitätsfrist beginnt die Verarbeitung
|
||||
|
||||
Szenario: Beim Start vorhandene Dateien werden übernommen
|
||||
Angenommen im Import-Ordner liegen Dateien, während das System nicht läuft
|
||||
Wenn das System startet
|
||||
Dann werden diese Dateien durch den Start-Scan erkannt und verarbeitet
|
||||
|
||||
Szenario: Periodischer Rescan fängt verpasste Ereignisse
|
||||
Angenommen eine Datei wurde vom Dateisystem-Watcher nicht gemeldet
|
||||
Wenn der periodische Rescan läuft
|
||||
Dann wird die Datei erkannt und verarbeitet
|
||||
|
||||
Szenario: Duplikat aus dem Import-Ordner wird aussortiert
|
||||
Angenommen ein bereits importiertes Dokument mit bekanntem SHA-256-Hash
|
||||
Wenn eine byte-identische Datei im Import-Ordner gespeichert wird
|
||||
Dann wird die Datei nach import/rejected/ verschoben
|
||||
Und eine Warnung mit der existing_document_id wird geloggt
|
||||
Und es wurde kein neues Aggregat angelegt
|
||||
|
||||
Szenario: Nicht unterstützte und versteckte Dateien werden ignoriert
|
||||
Angenommen der Watcher überwacht den Import-Ordner
|
||||
Wenn eine .docx-, .part- oder versteckte Datei dort gespeichert wird
|
||||
Dann wird sie ignoriert und verbleibt unverändert
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Paket `internal/importer`) |
|
||||
|---|---|
|
||||
| Neue Datei wird automatisch erkannt und verarbeitet | `TestImporter_NewFileIsIngested` |
|
||||
| Unvollständig geschriebene Datei wird nicht vorzeitig übernommen | `TestImporter_WaitsForStableFile` |
|
||||
| Beim Start vorhandene Dateien werden übernommen | `TestImporter_StartupScanPicksUpExistingFiles` |
|
||||
| Periodischer Rescan fängt verpasste Ereignisse | `TestImporter_RescanPicksUpMissedFile` |
|
||||
| Duplikat aus dem Import-Ordner wird aussortiert | `TestImporter_DuplicateMovedToRejected` |
|
||||
| Nicht unterstützte und versteckte Dateien werden ignoriert | `TestImporter_IgnoresUnsupportedAndHiddenFiles` |
|
||||
@@ -0,0 +1,58 @@
|
||||
# Feature: Textextraktion und OCR
|
||||
|
||||
Als System möchte ich den Textinhalt jedes Dokuments gewinnen,
|
||||
ohne die Originaldatei jemals zu verändern (DEC-IMMUTABLE-01),
|
||||
damit Suche und Anreicherung auf externem Kontext arbeiten können.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Textextraktion und OCR
|
||||
|
||||
Szenario: Born-digital-PDF braucht kein OCR
|
||||
Angenommen ein PDF mit maschinenlesbarem Textlayer
|
||||
Wenn die Extract-Stage läuft
|
||||
Dann wird der Text per pdftotext gewonnen
|
||||
Und ein Event "TextExtracted" mit needs_ocr=false wird aufgezeichnet
|
||||
Und die OCR-Stage wird übersprungen
|
||||
|
||||
Szenario: Gescanntes PDF wird per OCR erschlossen
|
||||
Angenommen ein PDF ohne bzw. mit unzureichendem Textlayer
|
||||
Wenn die OCR-Stage läuft
|
||||
Dann wird der Text per ocrmypdf im Sidecar-Modus gewonnen
|
||||
Und ein Event "OCRCompleted" mit engine "ocrmypdf" wird aufgezeichnet
|
||||
Und es wird kein Ausgabe-PDF erzeugt
|
||||
|
||||
Szenario: Bilddateien werden immer per OCR erschlossen
|
||||
Angenommen ein importiertes JPG-, PNG- oder HEIC-Bild
|
||||
Wenn die Verarbeitung läuft
|
||||
Dann liefert die Extract-Stage needs_ocr=true
|
||||
Und der Text wird per tesseract direkt aus dem Bild gewonnen
|
||||
|
||||
Szenario: HEIC wird nur temporär konvertiert
|
||||
Angenommen ein importiertes HEIC-Bild
|
||||
Wenn die OCR-Stage läuft
|
||||
Dann wird für die Erkennung ein temporäres PNG erzeugt
|
||||
Und das temporäre PNG wird nach der Erkennung gelöscht
|
||||
Und die archivierte Datei ist das unveränderte HEIC-Original
|
||||
|
||||
Szenario: Originaldatei bleibt byte-identisch
|
||||
Angenommen ein Dokument mit bekanntem SHA-256-Hash bei der Aufnahme
|
||||
Wenn das Dokument vollständig verarbeitet und abgelegt wurde
|
||||
Dann ist der SHA-256-Hash der archivierten Datei identisch mit dem Hash bei der Aufnahme
|
||||
|
||||
Szenario: Korrupte oder passwortgeschützte Datei schlägt endgültig fehl
|
||||
Angenommen eine nicht lesbare oder passwortgeschützte PDF-Datei
|
||||
Wenn Extraktion und OCR fehlschlagen
|
||||
Dann wird ein Event "ProcessingFailed" mit retryable=false aufgezeichnet
|
||||
Und das Dokument hat den Status "failed" mit gesetzter failed_stage
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Pakete `internal/pdf`, `internal/ocr`, `internal/pipeline`) |
|
||||
|---|---|
|
||||
| Born-digital-PDF braucht kein OCR | `TestExtractStage_TextPDFSkipsOCR` |
|
||||
| Gescanntes PDF wird per OCR erschlossen | `TestOCRStage_ScannedPDFUsesSidecarOnly` |
|
||||
| Bilddateien werden immer per OCR erschlossen | `TestExtractStage_ImageAlwaysNeedsOCR` |
|
||||
| HEIC wird nur temporär konvertiert | `TestOCR_HEICTempConversionIsCleanedUp` |
|
||||
| Originaldatei bleibt byte-identisch | `TestPipeline_OriginalHashUnchangedAfterProcessing` |
|
||||
| Korrupte oder passwortgeschützte Datei schlägt endgültig fehl | `TestPipeline_CorruptPDFFailsNonRetryable` |
|
||||
@@ -0,0 +1,47 @@
|
||||
# Feature: Optionale LLM-Anreicherung
|
||||
|
||||
Als Nutzer möchte ich, dass Dokumente semantisch angereichert werden (Titel, Zusammenfassung,
|
||||
Tags, Dokumentdatum, Sprache), ohne dass ein nicht erreichbares LLM die Verarbeitung blockiert.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Optionale LLM-Anreicherung
|
||||
|
||||
Szenario: Erfolgreiche Anreicherung
|
||||
Angenommen ein Dokument mit extrahiertem Text und ein erreichbares LLM
|
||||
Wenn die Enrich-Stage läuft
|
||||
Dann wird ein Event "DocumentEnriched" mit title, summary, tags, doc_date und language aufgezeichnet
|
||||
Und das verwendete Modell und die Prompt-Version sind im Event dokumentiert
|
||||
|
||||
Szenario: LLM nicht erreichbar führt zu Wiederholung mit Backoff
|
||||
Angenommen das LLM ist nicht erreichbar
|
||||
Wenn die Enrich-Stage fehlschlägt
|
||||
Dann wird ein Event "ProcessingFailed" mit retryable=true aufgezeichnet
|
||||
Und der nächste Versuch wird mit exponentiellem Backoff eingeplant
|
||||
|
||||
Szenario: Nach erschöpften Versuchen läuft die Pipeline weiter
|
||||
Angenommen das LLM bleibt über die maximale Versuchszahl hinaus nicht erreichbar
|
||||
Wenn der letzte Versuch fehlschlägt
|
||||
Dann wird ein Event "EnrichmentSkipped" aufgezeichnet
|
||||
Und das Dokument wird trotzdem indexiert und abgelegt
|
||||
|
||||
Szenario: Unbrauchbare LLM-Antwort ist wiederholbar
|
||||
Angenommen das LLM liefert kein valides JSON
|
||||
Wenn die Enrich-Stage die Antwort nicht parsen kann
|
||||
Dann wird der Fehler als retryable behandelt
|
||||
|
||||
Szenario: Übersprungene Anreicherung ist nachholbar
|
||||
Angenommen ein abgelegtes Dokument mit "EnrichmentSkipped"
|
||||
Wenn POST /v1/documents/{id}/reprocess mit from_stage "enrich" aufgerufen wird
|
||||
Dann wird nur die Anreicherung erneut ausgeführt
|
||||
Und bereits abgeschlossene Schritte laufen nicht erneut
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Pakete `internal/llm`, `internal/pipeline`) |
|
||||
|---|---|
|
||||
| Erfolgreiche Anreicherung | `TestEnrichStage_SuccessEmitsDocumentEnriched` |
|
||||
| LLM nicht erreichbar führt zu Wiederholung mit Backoff | `TestEnrichStage_UnreachableLLMIsRetryableWithBackoff` |
|
||||
| Nach erschöpften Versuchen läuft die Pipeline weiter | `TestPipeline_EnrichExhaustedSkipsAndContinues` |
|
||||
| Unbrauchbare LLM-Antwort ist wiederholbar | `TestEnricher_MalformedJSONIsRetryable` |
|
||||
| Übersprungene Anreicherung ist nachholbar | `TestReprocess_FromEnrichOnlyRunsEnrich` |
|
||||
@@ -0,0 +1,58 @@
|
||||
# Feature: Volltextsuche und Index-Rebuild
|
||||
|
||||
Als API-Client möchte ich Dokumente über ihren Inhalt und ihre Metadaten finden
|
||||
und den Suchindex jederzeit neu aufbauen können.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Volltextsuche
|
||||
|
||||
Szenario: Dokument ist über seinen Inhalt findbar
|
||||
Angenommen ein verarbeitetes Dokument, dessen Text "Stromabrechnung Stadtwerke" enthält
|
||||
Wenn GET /v1/search?q=stadtwerke aufgerufen wird
|
||||
Dann enthält das Ergebnis das Dokument mit id, title, original_name, archive_path und snippet
|
||||
|
||||
Szenario: Suche berücksichtigt Metadaten
|
||||
Angenommen ein angereichertes Dokument mit dem Tag "versicherung"
|
||||
Wenn GET /v1/search?q=versicherung aufgerufen wird
|
||||
Dann wird das Dokument gefunden, auch wenn der Begriff nicht im Text vorkommt
|
||||
|
||||
Szenario: Umlaute und Diakritika werden tolerant behandelt
|
||||
Angenommen ein Dokument, dessen Text "Gebührenbescheid" enthält
|
||||
Wenn nach "gebuhrenbescheid" gesucht wird
|
||||
Dann wird das Dokument gefunden
|
||||
|
||||
Szenario: Treffer sind nach Relevanz sortiert
|
||||
Angenommen mehrere Dokumente enthalten den Suchbegriff unterschiedlich häufig
|
||||
Wenn gesucht wird
|
||||
Dann sind die Treffer nach BM25-Relevanz absteigend sortiert
|
||||
|
||||
Szenario: OCR-Inhalte sind durchsuchbar
|
||||
Angenommen ein gescanntes Dokument, dessen Text nur per OCR gewonnen wurde
|
||||
Wenn nach einem Begriff aus dem OCR-Text gesucht wird
|
||||
Dann wird das Dokument gefunden
|
||||
|
||||
Funktionalität: Index-Rebuild über die API
|
||||
|
||||
Szenario: Rebuild stellt einen konsistenten Index wieder her
|
||||
Angenommen ein inkonsistenter oder leerer FTS-Index bei gefüllter documents-Projektion
|
||||
Wenn POST /v1/admin/search/rebuild aufgerufen wird
|
||||
Dann wird der Index vollständig aus der Projektion neu aufgebaut
|
||||
Und alle zuvor findbaren Dokumente sind wieder findbar
|
||||
|
||||
Szenario: Rebuild erfordert Authentifizierung
|
||||
Angenommen kein oder ein falscher API-Token
|
||||
Wenn POST /v1/admin/search/rebuild aufgerufen wird
|
||||
Dann antwortet das System mit 401
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Pakete `internal/search`, `internal/api`) |
|
||||
|---|---|
|
||||
| Dokument ist über seinen Inhalt findbar | `TestSearch_FindsDocumentByContent` |
|
||||
| Suche berücksichtigt Metadaten | `TestSearch_FindsDocumentByTag` |
|
||||
| Umlaute und Diakritika werden tolerant behandelt | `TestSearch_DiacriticsInsensitive` |
|
||||
| Treffer sind nach Relevanz sortiert | `TestSearch_ResultsOrderedByBM25` |
|
||||
| OCR-Inhalte sind durchsuchbar | `TestSearch_FindsOCRContent` |
|
||||
| Rebuild stellt einen konsistenten Index wieder her | `TestSearchRebuild_RestoresConsistentIndex` |
|
||||
| Rebuild erfordert Authentifizierung | `TestSearchRebuild_RequiresAuth` |
|
||||
@@ -0,0 +1,48 @@
|
||||
# Feature: Finale Ablage in Jahresordnern
|
||||
|
||||
Als Nutzer möchte ich, dass verarbeitete Dokumente unverändert und nachvollziehbar benannt
|
||||
in einer Jahresstruktur abgelegt werden und über die UUID im Dateinamen jederzeit
|
||||
ihrem Kontext in der Datenbank zuzuordnen sind.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Finale Ablage
|
||||
|
||||
Szenario: Verarbeitetes Dokument landet im Jahresordner
|
||||
Angenommen ein am 2026-07-12 importiertes Dokument
|
||||
Wenn die File-Stage läuft
|
||||
Dann liegt die Datei unter archive/2026/
|
||||
Und ein Event "DocumentFiled" mit dem relativen archive_path wird aufgezeichnet
|
||||
Und die Datei liegt nicht mehr im Staging
|
||||
|
||||
Szenario: Dateiname enthält UUID und optional den Importzeitstempel
|
||||
Angenommen die Konfiguration filename_timestamp=true
|
||||
Wenn ein Dokument "Rechnung Stadtwerke.pdf" abgelegt wird
|
||||
Dann entspricht der Dateiname dem Muster {JJJJMMTT-HHMMSS}_rechnung-stadtwerke_{uuid}.pdf
|
||||
Und bei filename_timestamp=false entfällt nur das Zeitstempel-Präfix
|
||||
|
||||
Szenario: Jahresordner folgt dem Importzeitpunkt
|
||||
Angenommen ein Dokument mit LLM-erkanntem doc_date "2019-03-01", importiert 2026
|
||||
Wenn es abgelegt wird
|
||||
Dann liegt es in archive/2026/ (Importjahr, nicht Dokumentjahr)
|
||||
|
||||
Szenario: Ablage über Volume-Grenzen ist atomar sicher
|
||||
Angenommen Staging und Archiv liegen auf unterschiedlichen Dateisystemen
|
||||
Wenn die File-Stage läuft
|
||||
Dann wird per Kopie mit fsync und anschließendem Rename verschoben
|
||||
Und zu keinem Zeitpunkt ist im Archiv eine unvollständige Datei sichtbar
|
||||
|
||||
Szenario: Datei ist über die API abrufbar
|
||||
Angenommen ein abgelegtes Dokument
|
||||
Wenn GET /v1/documents/{id}/file aufgerufen wird
|
||||
Dann wird die unveränderte Originaldatei mit korrektem Content-Type gestreamt
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Pakete `internal/storage`, `internal/pipeline`, `internal/api`) |
|
||||
|---|---|
|
||||
| Verarbeitetes Dokument landet im Jahresordner | `TestFileStage_MovesToYearFolder` |
|
||||
| Dateiname enthält UUID und optional den Importzeitstempel | `TestFiler_FilenamePatternWithUUIDAndTimestamp` |
|
||||
| Jahresordner folgt dem Importzeitpunkt | `TestFiler_YearFromImportDateNotDocDate` |
|
||||
| Ablage über Volume-Grenzen ist atomar sicher | `TestStorage_CrossDeviceMoveIsAtomic` |
|
||||
| Datei ist über die API abrufbar | `TestGetFile_StreamsOriginal` |
|
||||
@@ -0,0 +1,61 @@
|
||||
# Feature: Nachvollziehbarkeit, Event Sourcing und Wiederaufnahme
|
||||
|
||||
Als Betreiber möchte ich jede Zustandsänderung eines Dokuments lückenlos nachvollziehen,
|
||||
den Zustand aus den Ereignissen rekonstruieren und fehlgeschlagene Verarbeitung
|
||||
gezielt wiederaufnehmen können.
|
||||
|
||||
```gherkin
|
||||
Funktionalität: Event Sourcing und Audit-Trail
|
||||
|
||||
Szenario: Jede Zustandsänderung ist als Event nachvollziehbar
|
||||
Angenommen ein vollständig verarbeitetes Dokument
|
||||
Wenn GET /v1/documents/{id}/events aufgerufen wird
|
||||
Dann enthält die Antwort die lückenlose Eventfolge von DocumentReceived bis DocumentFiled
|
||||
Und jedes Event enthält seq, event_type, payload und created_at
|
||||
|
||||
Szenario: Der Zustand ist aus den Events rekonstruierbar
|
||||
Angenommen die aufgezeichnete Eventfolge eines Dokuments
|
||||
Wenn die Events per Replay gefaltet werden
|
||||
Dann entspricht das Ergebnis exakt der documents-Projektion
|
||||
|
||||
Szenario: Konkurrierende Änderungen werden erkannt
|
||||
Angenommen zwei Schreiber mit derselben erwarteten Version eines Aggregats
|
||||
Wenn beide Events anhängen wollen
|
||||
Dann gelingt genau ein Append und der zweite erhält einen Konsistenzfehler
|
||||
|
||||
Szenario: Events und Projektion sind transaktional konsistent
|
||||
Angenommen ein Append von Events schlägt in der Projektion fehl
|
||||
Wenn die Transaktion zurückgerollt wird
|
||||
Dann sind weder Events noch Projektionsänderung gespeichert
|
||||
|
||||
Funktionalität: Wiederaufnahme nach Fehlern
|
||||
|
||||
Szenario: Neustart verliert keine Arbeit
|
||||
Angenommen ein Dokument war beim Absturz des Systems in Verarbeitung
|
||||
Wenn das System neu startet und der Claim-Lease abläuft
|
||||
Dann wird die Verarbeitung automatisch wiederaufgenommen
|
||||
|
||||
Szenario: Wiederaufnahme setzt an der richtigen Stelle an
|
||||
Angenommen ein Dokument mit fehlgeschlagener OCR-Stage und Status "failed"
|
||||
Wenn POST /v1/documents/{id}/reprocess aufgerufen wird
|
||||
Dann wird ein Event "RetryRequested" aufgezeichnet
|
||||
Und die Verarbeitung setzt bei der OCR-Stage wieder an
|
||||
Und bereits abgeschlossene Stages laufen nicht erneut
|
||||
|
||||
Szenario: Wiederaufnahme während laufender Verarbeitung wird abgelehnt
|
||||
Angenommen ein Dokument, das aktuell von einem Worker geclaimt ist
|
||||
Wenn POST /v1/documents/{id}/reprocess aufgerufen wird
|
||||
Dann antwortet das System mit 409
|
||||
```
|
||||
|
||||
## Traceability
|
||||
|
||||
| Szenario | Go-Test (Pakete `internal/events`, `internal/pipeline`, `internal/api`) |
|
||||
|---|---|
|
||||
| Jede Zustandsänderung ist als Event nachvollziehbar | `TestGetEvents_ReturnsCompleteTrail` |
|
||||
| Der Zustand ist aus den Events rekonstruierbar | `TestReplay_MatchesProjection` |
|
||||
| Konkurrierende Änderungen werden erkannt | `TestAppend_ConcurrencyConflictReturnsError` |
|
||||
| Events und Projektion sind transaktional konsistent | `TestAppend_RollbackLeavesNoPartialState` |
|
||||
| Neustart verliert keine Arbeit | `TestQueue_ExpiredLeaseIsReclaimed` |
|
||||
| Wiederaufnahme setzt an der richtigen Stelle an | `TestReprocess_ResumesFromFailedStage` |
|
||||
| Wiederaufnahme während laufender Verarbeitung wird abgelehnt | `TestReprocess_ClaimedDocumentReturns409` |
|
||||
@@ -0,0 +1,27 @@
|
||||
# Anforderungen als Gherkin-Szenarien
|
||||
|
||||
Anforderungen werden als Gherkin-Szenarien (deutsche Schlüsselwörter: *Angenommen / Wenn / Dann*)
|
||||
dokumentiert und im Sinne von TDD umgesetzt (DEC-REQ-01, siehe [architecture.md](../architecture.md)):
|
||||
|
||||
1. **Szenario schreiben** (hier, vor der Implementierung)
|
||||
2. **Roten Test schreiben** — Stdlib `testing`, Testname = Szenario-Referenz aus der Traceability-Tabelle
|
||||
3. **Implementieren**, bis der Test grün ist
|
||||
|
||||
Regeln:
|
||||
|
||||
- Jedes Szenario hat genau einen gleichnamigen Go-Test; die Zuordnung steht in der
|
||||
Traceability-Tabelle am Ende jeder Datei.
|
||||
- Wird ein Test geändert, wird das Szenario im selben Commit nachgezogen (Drift-Kontrolle).
|
||||
- Szenarien beschreiben fachliches Verhalten, keine Implementierungsdetails.
|
||||
|
||||
## Übersicht
|
||||
|
||||
| Datei | Feature |
|
||||
|---|---|
|
||||
| [01-import-api.md](01-import-api.md) | Dokumenten-Upload über die API |
|
||||
| [02-import-ordner.md](02-import-ordner.md) | Automatischer Import aus dem überwachten Ordner |
|
||||
| [03-textextraktion-ocr.md](03-textextraktion-ocr.md) | Textextraktion und OCR (PDF + Bilder), Unversehrtheit der Originale |
|
||||
| [04-llm-anreicherung.md](04-llm-anreicherung.md) | Optionale LLM-Anreicherung |
|
||||
| [05-volltextsuche.md](05-volltextsuche.md) | Volltextsuche und Index-Rebuild |
|
||||
| [06-ablage.md](06-ablage.md) | Finale Ablage in Jahresordnern |
|
||||
| [07-nachvollziehbarkeit.md](07-nachvollziehbarkeit.md) | Event Sourcing, Audit-Trail, Wiederaufnahme |
|
||||
Reference in New Issue
Block a user