Doc-Upload-UI Pre-Flight Discovery — 2026-05-10¶
Erstellt: 2026-05-10 (Karte: Doc-Upload-UI, Tudidi-Task #48)
Branch: platform/k-doc-upload-ui (Phase 0 — Stop nach Discovery)
Outcome: ✋ STOP — Backend-API fehlt entgegen Karte-B-Annahme. Karten-Stop-Bedingung getriggert; kein Code geschrieben.
Zusammenfassung¶
| Item | Wert |
|---|---|
| Karte-B-Annahme | "Backend existiert laut Karte B" |
| Realität | Service-Layer existiert, HTTP-Route fehlt komplett |
| Discovery-Aufwand | ~30min (vs. Cap 45min) |
| Saga-Lehre 1 angewendet | ✓ — Phase-0-Discovery hat Drift entdeckt vor Code-Schreiben |
| Stop-Bedingung | ✓ getriggert: "Falls Backend-API nicht existiert: STOP, Backend-Karte vorher" |
Backend-API-Inventur¶
Was existiert (Service-Layer, Block 4 Phase B)¶
| Komponente | Pfad | Status |
|---|---|---|
DocumentUploader.upload(tenant_id, chatbot_id, document, chunks) |
src/kora_platform/services/document_uploader.py |
✅ Service-Methode (K-1c dual-vector) |
DoclingConverter (PDF → Markdown-Document) |
src/kora_platform/pipelines/components/docling_converter.py |
✅ Haystack-Component |
SemanticChunker (Document → Chunks) |
src/kora_platform/pipelines/components/semantic_chunker.py |
✅ Haystack-Component |
EmbedderClient.embed_passages_hybrid |
src/kora_platform/services/embedder.py |
✅ BGE-M3 dense + sparse |
QdrantManager (Collection + Upsert) |
src/kora_platform/services/qdrant_manager.py |
✅ Wire-Contract DENSE/SPARSE |
chatbot_sources DB-Tabelle (sync_status / last_synced_at / last_error) |
src/kora_platform/db/models/connector.py:95 |
✅ Schema vorhanden, 0 Rows |
SyncJob DB-Tabelle |
src/kora_platform/db/models/connector.py:141 |
✅ Schema vorhanden |
DI: get_document_uploader |
src/kora_platform/api/dependencies/uploader.py:24 |
✅ FastAPI-Dependency-Provider |
| Tests: DocumentUploader-Service | tests/unit/test_document_uploader.py (291 LoC) |
✅ |
Was fehlt (HTTP-Layer + Orchestrierung)¶
| Komponente | Status |
|---|---|
HTTP-Route POST /api/v1/tenants/me/chatbots/{chatbot_id}/sources/upload (oder analog) |
❌ |
HTTP-Route GET .../sources/{source_id} für Status-Polling |
❌ |
| Pipeline-Orchestrierung: UploadFile → DoclingConverter → SemanticChunker → DocumentUploader | ❌ |
chatbot_sources-Row-Lifecycle (creating → uploading → indexing → completed/failed) |
❌ |
connector_id="upload" als Stub-Connector-Eintrag in connectors-Tabelle |
❌ |
Audit-Action chatbot_source.created / .indexed / .failed |
❌ |
Operator-UI ConnectorsPage.vue — Stub mit "Verfügbar in Block 13" |
🔶 Existing als Placeholder |
UI-Framework-Inventur¶
| Item | Wert |
|---|---|
| Framework | Vue 3.4 + TypeScript 5.6 + Vite 5.4 |
| Router | vue-router 4.4 |
| Test-Framework | Vitest 2.1 (Unit) + Playwright 1.48 (e2e) |
| Form-Library | Keine — vanilla <form> + Composition-API |
| API-Client-Pattern | src/composables/useApi.ts (JSON-only Bearer-Auth Wrapper) |
| Pattern-Vorlage-Page | src/pages/ChatbotDetailPage.vue (Vue 3 Composition-API, onMounted(load), refs) |
| Pattern-Vorlage-Composable | src/composables/useChatbot.ts (CRUD-Composable mit ref/shallowRef) |
| File-Upload-Pattern | Keine — useApi muss um FormData-Variant erweitert oder eigener Helper gebraucht werden |
Roadmap-Verweis¶
Roadmap-Pfad-C-Definition (Z. 785–790):
Pfad C — Block 8.5 Stub-Variante (~6h Refined) Fixer Upload-Konnektor ohne
BaseConnector-Framework. Brücke zwischen v1.2.0 und Block 13. Risiko: Sunk-Cost-Falle, falls Block 13 dann anders strukturiert. Lohnt nur, wenn Source-Upload demnächst customer-relevant ist.
Pfad-C-Scope umfasst Backend-Stub + UI, nicht nur UI. Karte-B-Skelett ("Backend existiert, UI ergänzt") war eine Über-Vereinfachung dieser Realität.
Empfehlung für nächste Karten-Sequenz¶
Drei Optionen, kalibriert an Customer-Wert vs. Saga-Disziplin:
Option A — Zwei Karten (sauberster Pfad)¶
- Karte A1: Doc-Upload Backend-Stub (~2–3h Real)
- HTTP-POST-Route mit
UploadFile-Param, Tenant-Scope-Guard - Pipeline-Orchestrierung File → Docling → Chunker → DocumentUploader
chatbot_sources-Row-Lifecycle +connectors-Stub-Eintragupload- GET-Status-Route mit Polling-Schema
- Audit-Actions
chatbot_source.created/.indexed/.failed -
Tests: pytest-Integration-Test mit fixtures
-
Karte A2: Doc-Upload-UI (~3–4h Real)
- DocumentUploadForm + UploadProgress + UploadStatus + UploadError
useUpload-Composable mit FormData (erweitertuseApioder eigen)- Status-Polling-Loop auf neue Backend-Route
- Vitest-Unit-Tests + Visual-Smoke
Total: ~5–7h auf zwei Karten, klare Reviewability, jede Karte unter Saga-Lehre-13-Cap.
Option B — Eine Full-Stack-Karte mit erweitertem Cap¶
- 6h-Cap bleibt, aber Risiko erhöht
- Discovery hat ~30min verbraucht, Rest 5.5h: Backend (~2.5h) + UI (~2.5h) + Tests/Smoke (~30min) — sehr knapp
- Bei Pattern-Reife-Quote >50% machbar, aber kein Puffer für API-Drift / Pipeline-Discovery
- Saga-Lehre 13: bei Überschreitung Teil-Merge mit klarer Restkarten-Definition
Risiko: Customer-Wert in einer Karte, aber Saga-Disziplin lockerer.
Option C — STOP mit User-Entscheidung (gewählt)¶
- Karte-Stop-Bedingung wurde explizit definiert
- Discovery-Bilanz dokumentiert (diese Datei)
- Kein Code in dieser Karte; Tudidi #48 bleibt pending
- User entscheidet zwischen Option A und Option B für nächste Karte
- Discovery-Aufwand ist nicht verloren — alle Pfade nutzen ihn als Eingabe
Vorteil: Sauber an Karten-Definition gehalten. Kein impliziter Scope-Sweep. User-Sign-off vor Implementation.
Vorgeschlagene nächste Karten-Definition¶
Falls Option A gewählt:
Karte A1 — Doc-Upload Backend-Stub (Block 8.5 Pfad C, Backend-Anteil)
name: doc-upload-backend-stub
klasse: Backend-Stub (Block 8.5 Pfad C)
scope: |
POST /api/v1/tenants/me/chatbots/{chatbot_id}/sources/upload
Multipart-Form-Data mit File (PDF/MD/TXT/DOCX). Pipeline-
Orchestrierung File → DoclingConverter → SemanticChunker →
DocumentUploader.upload. chatbot_sources-Row-Lifecycle:
sync_status uploading → indexing → completed / failed.
GET .../sources/{source_id} für Status-Polling. Stub-Connector-
Row "upload" in connectors-Tabelle (Migration 010).
Audit-Actions chatbot_source.created/.indexed/.failed.
aufwand_schaetzung: 3-4h Refined / 2-3h Real (Pattern-Reife: chatbots.py
als Vorlage; existing Service-Layer)
abhaengigkeiten:
- DocumentUploader ✅
- DoclingConverter ✅
- SemanticChunker ✅
- chatbot_sources Schema ✅
- Audit-Helper ✅
risiko: niedrig-mittel (Pipeline-Orchestrierung neu, aber Komponenten erprobt)
saga_lehren_anwendbar:
- 1 (Phase-0-Discovery)
- 13 (Cleanup-Disziplin mit Sub-Phasen)
validation:
- POST mit Test-PDF schreibt Qdrant-Points + chatbot_sources-Row
- GET zeigt sync_status-Transition korrekt
- Audit-Eintrag pro State-Transition
- Cross-Tenant-Isolation: Tenant-A kann nicht in Chatbot-Tenant-B uploaden
stop_bedingungen:
- Pipeline-Komponenten-Inkompatibilität entgegen Discovery
- chatbot_sources-Schema-Drift entgegen Discovery
akut_indikator: 🔴 (Voraussetzung für UI-Karte A2)
Karte A2 — Doc-Upload-UI (im Anschluss an A1, Skelett aus Karte B unverändert übernehmbar)
Status nach Discovery¶
- Doc-Upload-UI-Karte als Full-UI-Karte vertagt bis Backend-Stub steht
- Discovery-Bilanz als Pre-Flight-Datapoint persistiert
- Tudidi #48 bleibt pending mit Note: "Stop nach Phase 0 Discovery — Backend-Route fehlt, A1+A2-Aufteilung empfohlen"
- Backlog-Inventur-Eintrag (Karte B, Item 3) wird in Folge-Karte mit zwei Karten ersetzt
- Saga-Lehre 1 in Aktion: Phase-0-Discovery zahlt sich aus — Code-Schreiben mit falscher Backend-Annahme hätte 2-3h verbraucht bevor das Loch aufgefallen wäre
Pattern: Saga-Lehre 1 (Phase-0-Discovery vor Code), Saga-Lehre 13 (strikte Stop-Bedingungen statt Scope-Sweep). Discovery-Aufwand ~30min ist Foundation-Investment für Karte A1+A2.