# Buying Agent – Frontend MVP Specification v0.3 **Status:** Review Candidate – vor Implementierung erneut durch den Coding-Agenten zu prüfen **Spezifikationsversion:** 0.3 **Stand:** 2026-10-04 **Ersetzt vollständig:** `buying-agent-frontend-mvp-spec-v0.2.md` **Gilt zusammen mit:** 1. `buying-agent-v0.1-spec-v1.1.md` 2. `buying-agent-architecture-addendum-v1.md` 3. `buying-agent-source-discovery-spec-v0.1.md` Bei Widersprüchen innerhalb des Frontend-/MVP-Bereichs hat diese v0.3 Vorrang. Sie präzisiert außerdem die für den Frontend-MVP notwendigen Backend-Verträge. --- # 1. Zielbild Der Buying Agent soll als **nutzbare PWA** einen vollständigen vertikalen Nutzerfluss abbilden. Der Nutzer soll ohne technische Kenntnisse: ```text BuyingProject anlegen ↓ Produktkategorie auswählen ↓ verfügbare Quellen für diese Kategorie bestimmen ↓ verfügbare Kriterien aus der Source-Taxonomie laden ↓ gewünschte Suchparameter auswählen und Werte setzen ↓ externe Suche generieren ↓ SearchRun starten / Status verfolgen ↓ Kandidaten sehen ↓ Shortlist / Ranking sehen, soweit konfiguriert ↓ Kandidat öffnen ↓ Attribute, Preise, Historie und bisherigen Status sehen ↓ Kriterien anpassen ↓ neue Suche ausführen ``` Die Oberfläche soll einfach bleiben. Die technische Komplexität verbleibt im Backend. --- # 2. Grundsätzliche Architektur ## 2.1 Backend bleibt System of Record Das Frontend enthält keine fachliche Kernlogik. Es konsumiert ausschließlich die versionierte `/api/v1`-API. Fachliche Entscheidungen über: - Taxonomie - Mapping - Query-Generierung - Produktidentität - Diffing - Ranking - Historisierung - Lifecycle liegen im Backend. ## 2.2 Keine Canonical-Criteria-Schicht Es wird **keine separate globale Canonical-Criteria-Ontologie** eingeführt. Für eine unterstützte Source/Kategorie bildet deren gelernte Taxonomie zunächst den primären Kriterienkatalog. Jedes Source-Kriterium erhält lediglich eine stabile interne ID: ```text CriterionDefinition.id = interne UUID ``` Diese ID dient als Persistenz- und Referenzidentität. Sie ist keine Behauptung, dass das Kriterium global quellenunabhängig kanonisch sei. ## 2.3 Source-Kriterien und Ergebnisattribute werden verbunden Ein Kriterium muss sowohl mit dem Suchfilter als auch mit den aus Suchergebnissen bzw. Produktdetailseiten extrahierten Attributen verbunden werden können. Beispiel: ```text CriterionDefinition id = origin = SOURCE source = geizhals category = notebook label = "Arbeitsspeicher" SourceFilterMapping: Geizhals Filter 17023 -> CriterionDefinition.id SourceAttributeMapping: "RAM" -> CriterionDefinition.id "Arbeitsspeicher" -> CriterionDefinition.id ggf. source_attribute_id -> CriterionDefinition.id ``` Eine Produktbeobachtung soll dann auf dieselbe interne CriterionDefinition referenzieren: ```text ProductAttributeObservation - source_product_reference_id - criterion_definition_id - raw_name - raw_value - normalized_value - unit - observed_at - search_run_id ``` ## 2.4 INTERNAL-Kriterien Zusätzliche interne Kriterien werden nur angelegt, wenn für die Kaufentscheidung relevante Eigenschaften von der Source-Taxonomie nicht angeboten werden. Beispiele: - externer Akku-Benchmark - Reparierbarkeit - Testscore - Lautstärke unter Last ```text CriterionDefinition.origin = INTERNAL ``` Diese besitzen naturgemäß keinen Source-Filter, können aber später von anderen Datenquellen befüllt werden. --- # 3. Produktkategorien ## 3.1 Interner Katalog Das BuyingProject referenziert eine interne `ProductCategory`. Beispiel: ```text ProductCategory id = notebook name = Notebooks status = ACTIVE ``` Erforderlicher Endpunkt: ```text GET /api/v1/product-categories ``` Response mindestens: ```text id name status ``` Der initiale Pilot enthält mindestens: ```text notebook ``` ## 3.2 Source-Zuordnung Die Source-spezifische Kategorie wird separat gemappt: ```text SourceCategory source = geizhals external_category_key = nb product_category_id = notebook ``` Damit ist das BuyingProject nicht direkt an `geizhals/nb` gekoppelt. --- # 4. BuyingProject Ein BuyingProject ist die konkrete Instanz eines Kaufvorhabens. Es kann **ohne ProductProfile** angelegt werden. Mindestens: ```text id workspace_id nullable name product_category_id market currency target_budget nullable shortlist_size status template_id nullable template_version nullable criteria_version created_at updated_at archived_at nullable ``` Status: ```text ACTIVE PURCHASED ARCHIVED ``` Default: ```text shortlist_size = 5 criteria_version = 1 ``` ## 4.1 Kein doppelter Suchpreis Ein extern wirksames Preismaximum wird **nicht zusätzlich als BuyingProject-Feld** gehalten. Wenn die Source einen Preisfilter unterstützt, wird das maximale Suchbudget als normales ProjectCriterion modelliert. `target_budget` bleibt optional als spätere Ranking-/Decision-Information. Damit existieren nicht zwei konkurrierende Wahrheiten für denselben externen Preisfilter. --- # 5. Rolle von ProductProfile / BuyingTemplate Das bisherige `ProductProfile` ist für neue BuyingProjects **nicht verpflichtend**. Fachlich wird es als optionale Vorlage verstanden. Zielbild: ```text BuyingTemplate "Mobiler Entwicklungsrechner" ↓ optional beim Projektstart BuyingProject ``` Eine Vorlage kann z. B. liefern: - vorgeschlagene Suchkriterien - Ranking-Konfiguration - Default-Zielbudget - Kategorie - Markt / Währung Beim Erstellen eines Projekts werden relevante Werte in die Projektkonfiguration übernommen. Spätere Änderungen an der Vorlage verändern bestehende Projekte **nicht rückwirkend**. Zur Nachvollziehbarkeit: ```text template_id template_version ``` Erforderlicher Endpunkt: ```text GET /api/v1/buying-templates ``` Response mindestens: ```text id version name product_category_id market currency description nullable ``` Die bestehende ProductProfile-Implementierung darf technisch weiterverwendet werden, solange sie nicht mehr als zwingende Voraussetzung für ein BuyingProject fungiert. --- # 6. ProjectCriteria und Versionierung ## 6.1 Zweck ProjectCriteria beschreiben die vom Nutzer gewählten Suchparameter eines BuyingProjects. Sie referenzieren die dynamisch geladenen CriterionDefinitions. ## 6.2 Immutable Versions ProjectCriteria werden nicht still überschrieben. Ein fachlich relevanter Criteria-Wechsel erzeugt eine neue `criteria_version`. Schema: ```text ProjectCriterion - id - project_id - criteria_version - criterion_definition_id - operator - value - enabled_for_discovery - created_at ``` Für eine Project-/Criteria-Version gilt: ```text (project_id, criteria_version, criterion_definition_id) ``` als fachlich eindeutige Kombination. `BuyingProject.criteria_version` zeigt auf die aktuell aktive Version. Änderung: ```text aktuelle criteria_version = 3 ↓ Nutzer ändert Kriterien ↓ neue immutable Kriterienmenge version 4 ↓ BuyingProject.criteria_version = 4 ``` SearchRuns und DiscoverySearches müssen die verwendete `criteria_version` speichern. ## 6.3 Standard-UI Die Standardoberfläche zeigt keine technischen Ranking-Parameter wie: - weight - acceptable - ideal - strategy - scoring_version Der Nutzer wählt lediglich: ```text Kriterium Operator Wert / Wertebereich für externe Suche aktiv ja/nein ``` Optional kann später eine einfache Kennzeichnung wie „Muss“ / „Wunsch“ ergänzt werden, sie ist für v0.3 nicht Voraussetzung. --- # 7. Ranking-Konfiguration Discovery und Ranking bleiben getrennte fachliche Bereiche. ProjectCriteria dürfen keine versteckten Scoring-Regeln erzwingen. Ein Projekt kann optional eine Ranking-Konfiguration besitzen. Mögliche Herkunft: 1. aus einem BuyingTemplate/ProductProfile übernommen 2. später explizit durch Advanced UI 3. später von KI vorgeschlagen 4. später durch eine dokumentierte Default-Strategie erzeugt Für den MVP gilt: - Ein Projekt ohne Ranking-Konfiguration darf trotzdem Discovery ausführen. - In diesem Zustand zeigt das Frontend Kandidaten, aber kennzeichnet Ranking als `NOT_CONFIGURED`. - Für den Notebook-Pilot kann das vorhandene Profil `mobile-development-notebook` als optionale Vorlage genutzt werden, damit ProductScore/Shortlist end-to-end getestet werden können. - Es dürfen keine stillen, nicht dokumentierten Scoring-Regeln erfunden werden. --- # 8. Brücke CriterionDefinition -> bestehende Ranking-Engine Die bestehende Ranking-/ProductProfile-Engine arbeitet weiterhin mit Attribut-Keys wie: ```text ram_gb storage_gb weight_kg ``` Dafür erhält `CriterionDefinition` optional: ```text scoring_attribute_key nullable ``` Beispiel: ```text CriterionDefinition.id = label = "Arbeitsspeicher" scoring_attribute_key = "ram_gb" ``` Wichtig: - `scoring_attribute_key` ist **kein Canonical-Criterion-Layer**. - Es ist ausschließlich ein technischer Adapter zur bestehenden Scoring-Engine. - Source-Kriterium und interne UUID bleiben die primäre Persistenzidentität. - Ist kein Ranking-Mapping vorhanden, bleibt `scoring_attribute_key = null`. - Ein fehlender Mapping-Key darf keine Discovery verhindern. So kann ein aus Geizhals beobachteter Wert: ```text criterion_definition_id = normalized_value = 32 ``` für einen Engine-Snapshot zusätzlich als: ```text ram_gb = 32 ``` bereitgestellt werden. --- # 9. Source-Auswahl im Wizard Der Nutzer soll die technische Source möglichst nicht auswählen müssen, solange nur eine geeignete Source existiert. Nach Auswahl einer ProductCategory fragt das Frontend: ```text GET /api/v1/product-categories/{product_category_id}/sources ``` Response mindestens: ```text source source_category_id display_name status ``` Regeln: - genau eine aktive Source -> automatisch auswählen - mehrere aktive Sources -> Source-Picker anzeigen - keine aktive Source -> verständlicher Empty State Der Nutzer muss die externe SourceCategory-ID wie `nb` nicht kennen. --- # 10. Criterion-Katalog vor Projektanlage Der Projekt-Wizard muss Kriterien laden können, bevor eine Project-ID existiert. Erforderliche Endpunkte: ```text GET /api/v1/product-categories GET /api/v1/product-categories/{product_category_id}/sources GET /api/v1/product-categories/{product_category_id}/available-criteria?source=geizhals ``` Backend löst intern: ```text ProductCategory + Source ↓ SourceCategory ↓ CriterionDefinitions / SourceFilterDefinitions ``` auf. `available-criteria` liefert mindestens: ```text criterion_definition_id label description nullable origin source product_category_id filter_type unit nullable operators options / range metadata mapping_status supports_discovery scoring_attribute_key nullable ``` Nur Source-Kriterien mit nutzbarer Filterdefinition werden als externe Suchparameter angeboten. INTERNAL-Kriterien dürfen separat sichtbar sein, wenn sie für die Kategorie bekannt sind. --- # 11. Taxonomie-Lernen Taxonomie wird on demand pro SourceCategory gelernt. Initial: ```text Geizhals -> Notebooks ``` Die gelernte Taxonomie soll später im Admin-Bereich bearbeitbar sein. Mapping-Status: ```text DISCOVERED LEARNED_UNVERIFIED VERIFIED BROKEN DISABLED ``` Nur `VERIFIED` darf automatisch für produktive Source-Query-Generierung verwendet werden. Technisch eindeutige automatische Verifikation ist zulässig, wenn der Backend-Service einen reproduzierbaren Roundtrip sicher nachweisen kann. --- # 12. DiscoverySearch Persistenz bleibt **1:n**: ```text BuyingProject └── DiscoverySearch[] ``` Für den ersten MVP gilt als fachliche Einschränkung: ```text maximal eine aktive DiscoverySearch pro BuyingProject + Source + SourceCategory ``` Diese Einschränkung darf nicht als dauerhafte 1:1-Beziehung in das Datenmodell eingebaut werden. Mindestens: ```text id buying_project_id source_id source_category_id criteria_version mapping_version generated_url status schedule_interval_minutes next_run_at nullable last_successful_run_at nullable created_at updated_at ``` ## 12.1 Scheduling Das Suchintervall gehört fachlich an die `DiscoverySearch`. `BuyingProject` hält **kein zweites maßgebliches schedule_interval_minutes**. Ein Template oder Projekt-Wizard darf einen Defaultwert vorgeben, dieser wird beim Anlegen der DiscoverySearch kopiert. Ab dann ist: ```text DiscoverySearch.schedule_interval_minutes ``` die maßgebliche Quelle. Default: ```text 180 Minuten ``` --- # 13. Statuswelten strikt trennen ## 13.1 Project Lifecycle ```text ACTIVE PURCHASED ARCHIVED ``` ## 13.2 DiscoverySearch Status ```text ACTIVE OUT_OF_SYNC BLOCKED DISABLED ``` ## 13.3 SearchRun Status ```text RUNNING SUCCESS PARTIAL FAILED BLOCKED TOO_BROAD ``` ## 13.4 Run Diff ```text NEW CHANGED UNCHANGED MISSING ``` ## 13.5 Product Lifecycle ```text ACTIVE DISAPPEARED ARCHIVED ``` ## 13.6 Identity / Matching ```text MATCHED MATCH_UNCERTAIN ``` Diese Status dürfen in DB, API und UI nicht zu einem gemeinsamen Statusfeld vermischt werden. --- # 14. Diff- und Lifecycle-Regeln Nur vollständige erfolgreiche Full Runs dürfen fehlende Produkte ableiten. ```text SUCCESS Full Run: - NEW - CHANGED - UNCHANGED - MISSING ``` `PARTIAL` darf **keine** MISSING-/DISAPPEARED-Transition auslösen. Lifecycle: ```text 1 vollständiger Run fehlend -> MISSING im Run-Diff 3 aufeinanderfolgende vollständige Runs fehlend -> Product Lifecycle DISAPPEARED ``` Wiederauftauchen reaktiviert bestehende SourceProductReference, sofern Identity ausreichend sicher ist. --- # 15. SearchRun Summary SearchRun-Responses und History-Listen müssen Aggregatzähler liefern. Mindestens: ```text id discovery_search_id started_at finished_at nullable status result_count page_count new_count changed_count unchanged_count missing_count error_code nullable ``` `disappeared_count` darf optional zusätzlich als Lifecycle-Aggregat geliefert werden, gehört aber nicht zum Run-Diff-Enum. --- # 16. ProductObservation und Attribute Eine Observation gehört mindestens zu: ```text SourceProductReference + SearchRun ``` Schema konzeptionell: ```text ProductObservation - id - source_product_reference_id - search_run_id - observed_at - best_price - currency - availability nullable - seller_count nullable ``` Dynamische Attribute: ```text ProductAttributeObservation - product_observation_id - criterion_definition_id - raw_name - raw_value - normalized_value - unit nullable ``` Damit ist jede Beobachtung eindeutig einem Produkt und einem konkreten SearchRun zugeordnet. --- # 17. Candidate Summary DTO Listen und Shortlist müssen genügend Daten liefern, damit das Frontend nicht für jede Zeile Einzelrequests benötigt. Mindestens: ```text candidate_id source_product_reference_id title manufacturer nullable model nullable source_url current_best_price nullable currency nullable current_dynamic_attributes product_score nullable product_eligibility nullable ranking_status shortlist_status current_diff_status product_lifecycle_status match_status first_seen_at last_seen_at ``` `current_dynamic_attributes` ist metadata-driven und keine feste Notebook-Struktur. --- # 18. Candidate Detail DTO Erforderlicher Endpunkt: ```text GET /api/v1/buying-projects/{project_id}/candidates/{candidate_id} ``` Mindestens: ```text Candidate Summary + alle aktuellen normalisierten Attribute + Source-Metadaten + aktuelle Observation + Ranking-/Eligibility-Daten, soweit vorhanden ``` Das Frontend darf keine Gründe/Empfehlungen behaupten, die nicht tatsächlich persistiert sind. --- # 19. Observation History Erforderlicher Endpunkt: ```text GET /api/v1/buying-projects/{project_id}/candidates/{candidate_id}/observations ``` Mindestens: ```text observation_id observed_at best_price currency availability nullable seller_count nullable dynamic_attributes search_run_id ``` Diese Historie ermöglicht: - Preisentwicklung - Attributänderungen - Last Seen - Verlauf über SearchRuns Kein HTML-Archiv. --- # 20. Project Summary DTO Die Projektliste soll nicht N+1-Requests erzeugen. Erforderlich: ```text GET /api/v1/buying-projects ``` Pro Projekt mindestens: ```text id name product_category status last_successful_run_at nullable next_run_at nullable discovery_status nullable candidate_count shortlist_count new_count changed_count missing_count current_search_result_count nullable ``` --- # 21. Projekt-Wizard Route: ```text /projects/new ``` ## Schritt 1 – Basis ```text Projektname Produktkategorie Markt Währung Zielbudget optional Shortlist-Größe optional BuyingTemplate ``` ## Schritt 2 – Source Frontend lädt: ```text GET /product-categories/{id}/sources ``` Bei genau einer Source automatische Auswahl. Bei mehreren Sources Auswahl durch Nutzer. ## Schritt 3 – Kriterien laden Frontend lädt: ```text GET /product-categories/{id}/available-criteria?source=... ``` Die UI zeigt Source-Kriterien als verständliche Liste. Beispiel Notebooks: ```text [ ] Hersteller [ ] Arbeitsspeicher [ ] SSD [ ] Gewicht [ ] Displaygröße [ ] CPU [ ] Grafikkarte ... ``` ## Schritt 4 – Parameter setzen Nach Aktivierung eines Kriteriums: ```text Arbeitsspeicher Operator: mindestens Wert: 24 GB ``` Control-Typen: ```text BOOLEAN ENUM MULTI_ENUM NUMERIC_RANGE NUMERIC_THRESHOLD TEXT ``` Das UI rendert dynamisch. Keine hartcodierten Notebook-Felder. ## Schritt 5 – Projekt speichern Erst jetzt wird das BuyingProject angelegt und die ProjectCriteria-Version gespeichert. Danach wird die initiale DiscoverySearch für die ausgewählte Source erzeugt. --- # 22. Kriterienänderung im bestehenden Projekt Route: ```text /projects/:projectId/criteria ``` Ändert der Nutzer relevante ProjectCriteria: ```text neue immutable criteria_version BuyingProject.criteria_version = neue Version DiscoverySearch.status = OUT_OF_SYNC ``` Das Frontend zeigt: > Suchparameter wurden geändert. Die externe Suche muss aktualisiert werden. Aktion: ```text Suche aktualisieren ``` Backend: ```text generate-query → VERIFIED Mappings verwenden → neue URL speichern → optional SearchRun starten ``` --- # 23. PATCH BuyingProject Erforderlicher Endpunkt: ```text PATCH /api/v1/buying-projects/{id} ``` Im MVP explizit änderbar: ```text name target_budget shortlist_size status ``` Nicht über diesen PATCH ändern: ```text product_category_id market currency criteria source source_category schedule_interval ``` Suchrelevante Änderungen laufen über die Criteria-/DiscoverySearch-Verträge. Falls Kategorie/Markt/Währung später änderbar werden, benötigt dies einen expliziten Migrations-/Reinitialisierungsflow und ist nicht Teil des MVP. --- # 24. Hauptscreens ## 24.1 Projektliste ```text /projects ``` Anzeige: - Name - Kategorie - Project Lifecycle - letzter erfolgreicher Run - Discovery-Status - Kandidatenzahl - Shortlistzahl - NEW / CHANGED Counts - nächste Suche ## 24.2 Projektdetail ```text /projects/:projectId ``` Bereiche: ```text Übersicht Kandidaten Shortlist Kriterien Historie ``` ## 24.3 Kandidaten ```text /projects/:projectId/candidates ``` Desktop: - Tabelle / kompakte Liste Mobile: - Cards / List Items ## 24.4 Kandidatendetail ```text /projects/:projectId/candidates/:candidateId ``` Zeigt nur tatsächlich vorhandene Daten: - Titel / Hersteller / Modell - aktueller Preis - dynamische Attribute - Eligibility / ProductScore, soweit vorhanden - Ranking Status - Diff Status - Product Lifecycle - Match Status - First Seen / Last Seen - Observation History Nicht anzeigen, solange Backend es nicht erzeugt: - Deep-Enrichment-Ergebnisse - Review-Zusammenfassungen - finale Kaufentscheidung - nicht persistierte Begründungen ## 24.5 Shortlist ```text /projects/:projectId/shortlist ``` Zeigt Top-N, falls Ranking konfiguriert. Ohne Ranking-Konfiguration: ```text Ranking ist noch nicht konfiguriert. ``` ## 24.6 Historie ```text /projects/:projectId/history ``` SearchRuns mit: ```text Zeitpunkt Run Status Trefferzahl Page Count NEW CHANGED UNCHANGED MISSING Fehler / BLOCKED / TOO_BROAD ``` --- # 25. Admin-Bereich Routen: ```text /admin/sources /admin/sources/:source/categories /admin/sources/:source/categories/:category ``` Funktionen: - Kategorien sehen - Taxonomie laden/neu laden - SourceFilterDefinitions sehen - Optionen / Ranges sehen - CriterionDefinitions sehen - SearchFilter-Mapping sehen - SourceAttributeMappings sehen - scoring_attribute_key sehen/pflegen, soweit administrativ vorgesehen - Mapping-Status ändern - kleine Mapping-Korrekturen durchführen - BROKEN / DISABLED erkennen Technische IDs sind nur hier sichtbar. Das Frontend generiert keine Source-URL-Encodings selbst. --- # 26. Frontend-Technologie Verbindlich: - React - TypeScript - Vite - PWA - TanStack Query - React Router oder vergleichbare schlanke Lösung - Playwright für E2E Nicht erforderlich: - Next.js - SSR - SEO --- # 27. Responsive Architektur Von Anfang an zwei bewusste Hauptlayouts. ## Desktop ```text Sidebar + Hauptinhalt + optional Detailbereich ``` ## Mobile ```text Bottom Navigation / kompakte Navigation + Vollbild-Detailseiten + projektinterne Tabs ``` Keine bloße Verkleinerung des Desktop-Layouts. --- # 28. Mitai-Bezug Wenn der Mitai-Quellcode dem Coding-Agenten im Repository oder über einen konkret angegebenen Pfad zugänglich ist, sollen passende Muster wiederverwendet werden: - PWA-Shell - Responsive Breakpoints - Navigation - Layout - ggf. Basiskomponenten Ohne zugänglichen Pfad darf der Agent keine Annahmen über konkrete Mitai-Komponenten treffen. Dann nur dieselben Architekturprinzipien umsetzen. --- # 29. CORS Backend muss den Frontend-Origin konfigurierbar zulassen. Keine harte Produktions-URL im Code. Beispiel: ```text CORS_ALLOWED_ORIGINS ``` Lokale und produktive Origins über Environment. --- # 30. Ports Ports sind Deployment-Konfiguration und keine Fachlogik. Sie werden über: - Docker Compose - `.env` - `.env.example` konfiguriert. Keine fachliche Spec darf einen unveränderlichen Frontend-Port voraussetzen. --- # 31. Docker Compose Der nutzbare MVP enthält mindestens: ```text api postgres frontend ``` Das Frontend muss über Root-Compose startbar sein. Development und Production Build dürfen unterschiedliche Docker Targets verwenden. --- # 32. CI CI muss mindestens enthalten: Backend: - Unit Tests - Integration Tests - API E2E Frontend: - TypeScript typecheck - lint - unit/component tests - production build E2E: - Playwright - vollständiger Notebook-Pilot mit Fixture/Mock-Source - Desktop viewport - Mobile viewport Live-Geizhals-Abfragen gehören nicht in CI. --- # 33. PWA Mindestens: - Manifest - installierbare App - App Icons - Responsive App Shell - Offline-Fallback Keine komplexe Offline-Synchronisation im MVP. --- # 34. Loading / Empty / Error States Jeder wesentliche Screen braucht: ```text Loading Empty Error Retry ``` Beispiele: - Noch keine Projekte vorhanden. - Noch keine Kandidaten gefunden. - Noch kein SearchRun ausgeführt. - Taxonomie wurde noch nicht geladen. - Keine Source für diese Produktkategorie verfügbar. - Suche ist OUT_OF_SYNC. - Suche wurde BLOCKED. - Ranking ist nicht konfiguriert. --- # 35. API-Verträge – Mindestumfang ## Produktkategorien ```text GET /api/v1/product-categories GET /api/v1/product-categories/{id}/sources GET /api/v1/product-categories/{id}/available-criteria?source=... ``` ## Templates ```text GET /api/v1/buying-templates ``` ## Projekte ```text GET /api/v1/buying-projects POST /api/v1/buying-projects GET /api/v1/buying-projects/{id} PATCH /api/v1/buying-projects/{id} ``` ## Kriterien ```text GET /api/v1/buying-projects/{id}/criteria PUT /api/v1/buying-projects/{id}/criteria ``` `PUT` erzeugt eine neue immutable criteria_version. ## Source / Taxonomie ```text GET /api/v1/sources GET /api/v1/sources/{source}/categories POST /api/v1/sources/{source}/categories/{category}/discover-taxonomy ``` ## Admin Mapping ```text GET /api/v1/sources/{source}/categories/{category}/filters GET /api/v1/criterion-mappings POST /api/v1/criterion-mappings PATCH /api/v1/criterion-mappings/{id} GET /api/v1/source-attribute-mappings POST /api/v1/source-attribute-mappings PATCH /api/v1/source-attribute-mappings/{id} ``` ## Discovery Search ```text GET /api/v1/buying-projects/{id}/discovery-searches POST /api/v1/buying-projects/{id}/discovery-searches POST /api/v1/discovery-searches/{id}/generate-query POST /api/v1/discovery-searches/{id}/run PATCH /api/v1/discovery-searches/{id} ``` `PATCH DiscoverySearch` darf im MVP mindestens ändern: ```text schedule_interval_minutes status ACTIVE/DISABLED ``` ## SearchRuns ```text GET /api/v1/discovery-searches/{id}/runs GET /api/v1/search-runs/{id} ``` ## Candidates ```text GET /api/v1/buying-projects/{id}/candidates GET /api/v1/buying-projects/{id}/candidates/{candidateId} GET /api/v1/buying-projects/{id}/candidates/{candidateId}/observations GET /api/v1/buying-projects/{id}/shortlist ``` --- # 36. Nutzerfreundlichkeit Die Standardoberfläche zeigt keine Begriffe wie: ```text CriterionDefinition SourceFilterDefinition mapping_version source_filter_id normalized_spec_fingerprint scoring_attribute_key ``` Stattdessen: ```text Arbeitsspeicher mindestens 24 GB ``` Technische Details gehören in den Admin-Bereich. --- # 37. KI-Erweiterung – bewusst vorbereitet, nicht implementiert Später soll KI den heute manuellen Kriterien-Auswahlschritt übernehmen können. Ziel: ```text Nutzer beschreibt Wunsch in natürlicher Sprache ↓ KI erhält verfügbare Source-Kriterien ↓ KI schlägt konkrete Filterparameter vor ↓ Nutzer bestätigt / korrigiert ↓ ProjectCriteria werden gespeichert ``` Wichtig: Die KI soll Source-Kriterien möglichst aus dem tatsächlich verfügbaren Katalog auswählen. INTERNAL-Kriterien dürfen nur vorgeschlagen werden, wenn sie im System bekannt sind und eine Datenquelle bzw. Bewertungsstrategie existiert. Keine KI-Implementierung in v0.3. --- # 38. Tests ## Frontend Unit / Component - dynamischer Criteria Renderer pro Filtertyp - ProductCategory Picker - automatische Source-Auswahl bei genau einer Source - Source Picker bei mehreren Sources - Projekt-Wizard - Mapping-/Statusdarstellung - Candidate List - Candidate Detail - Mobile Navigation - Desktop Navigation - Loading / Empty / Error States ## API Integration - ProductCategory-Katalog laden - Sources pro ProductCategory laden - available-criteria vor Projektanlage laden - Projekt ohne Profil anlegen - Projekt optional aus Template anlegen - Criteria versioniert speichern - DiscoverySearch anlegen - Suchintervall an DiscoverySearch ändern - Search generieren - SearchRun starten - SearchRun Summary laden - Candidate Summary laden - Candidate Detail laden - Observation History laden - Shortlist laden ## E2E Notebook-Pilot ```text App öffnen → neues Projekt → Notebooks wählen → verfügbare Source automatisch bestimmen → Geizhals-Kriterien laden → RAM >= 24 GB auswählen → Preismaximum als Source-Kriterium setzen → Projekt speichern → DiscoverySearch erzeugen → Query generieren → SearchRun ausführen → Kandidaten anzeigen → Kandidat öffnen → dynamische Attribute sehen → Preis / Observation History sehen → Kriterien ändern → neue criteria_version → OUT_OF_SYNC sehen → Query neu generieren ``` Wenn optional ein Ranking-Template verwendet wird: ```text → scoring_attribute_key Brücke verwenden → ProductScore sichtbar → Shortlist sichtbar ``` ## Responsive E2E Mindestens: - Desktop Viewport - Mobile Viewport --- # 39. Nicht Bestandteil dieses Slices - Auth / Login - RBAC - Einladungen - Multi-User UI - Keepa - Idealo - LLM-Automation - Deep Enrichment - finale Decision Engine - Kaufautomatisierung - Warenkorb - komplexe Benachrichtigungen Die Architektur darf diese Erweiterungen nicht verbauen. --- # 40. Definition of Done ## Architektur - [ ] Keine separate Canonical-Criteria-Schicht. - [ ] ProductCategory-Katalog existiert. - [ ] Source-Zuordnung pro ProductCategory existiert. - [ ] Source-Kriterien besitzen stabile interne CriterionDefinition-IDs. - [ ] SearchFilter und Ergebnisattribute können auf dieselbe CriterionDefinition-ID gemappt werden. - [ ] Optionales `scoring_attribute_key` verbindet Kriterien mit bestehender Ranking-Engine. - [ ] INTERNAL-Kriterien sind möglich. - [ ] BuyingProject ist ohne ProductProfile anlegbar. - [ ] ProductProfile kann optional als Vorlage dienen. - [ ] ProjectCriteria sind immutable versioniert. - [ ] SearchRun und DiscoverySearch referenzieren criteria_version. - [ ] DiscoverySearch-Persistenz bleibt 1:n-fähig. - [ ] Scheduling liegt fachlich an DiscoverySearch. - [ ] Statuswelten sind getrennt. ## API - [ ] `GET /product-categories` existiert. - [ ] Sources pro ProductCategory existieren. - [ ] available-criteria funktioniert vor Projektanlage. - [ ] BuyingTemplate-Liste existiert. - [ ] Candidate Summary DTO enthält Preis, Attribute, Diff, Lifecycle, last_seen und Ranking-Felder. - [ ] Candidate Detail existiert. - [ ] Observation History existiert. - [ ] SearchRun Summary enthält Diff-Aggregate. - [ ] Project Summary DTO enthält Aggregate. - [ ] PATCH-Felder sind explizit definiert. - [ ] CORS ist konfigurierbar. ## Frontend - [ ] React + TypeScript + Vite. - [ ] PWA installierbar. - [ ] Desktop-Layout. - [ ] Mobile-Layout. - [ ] Projektliste. - [ ] Projekt-Wizard. - [ ] ProductCategory Picker. - [ ] Source wird automatisch gewählt, wenn nur eine verfügbar ist. - [ ] Kriterien dynamisch aus Source-Taxonomie. - [ ] Keine notebook-spezifisch hartcodierten Criteria Controls. - [ ] Projekt-Detail. - [ ] Kandidatenliste. - [ ] Kandidatendetail. - [ ] Observation History. - [ ] Shortlist, wenn Ranking verfügbar. - [ ] Admin-Bereich für Taxonomie/Mappings. - [ ] OUT_OF_SYNC sichtbar und korrigierbar. - [ ] Loading / Empty / Error States. ## Integration - [ ] Frontend in Docker Compose. - [ ] Ports über Environment/Compose konfigurierbar. - [ ] Frontend Build/Tests in CI. - [ ] Backend Tests grün. - [ ] Frontend Tests grün. - [ ] Playwright Desktop E2E grün. - [ ] Playwright Mobile E2E grün. - [ ] Notebook-Pilot über die UI erfolgreich. --- # 41. Arbeitsauftrag an den Coding-Agenten Diese Datei ersetzt `buying-agent-frontend-mvp-spec-v0.2.md` vollständig. Bitte **noch nicht implementieren**. Führe erneut denselben Review-Prozess durch wie bei v0.1 und v0.2. Prüfe die aktuelle Repository-Implementierung gegen: 1. `buying-agent-v0.1-spec-v1.1.md` 2. `buying-agent-architecture-addendum-v1.md` 3. `buying-agent-source-discovery-spec-v0.1.md` 4. `buying-agent-frontend-mvp-spec-v0.3.md` Prüfe insbesondere explizit, ob: - die bisherigen F1–F6 geschlossen sind, - R1 ProductCategory-Katalog geschlossen ist, - R2 Source-Auswahl vor Projektanlage geschlossen ist, - R3 CriterionDefinition-UUID ↔ Scoring-Attribut-Key geschlossen ist, - R4 ProjectCriteria-Versionierung geschlossen ist, - die bisherigen kleineren Restlücken zu Template-API, PATCH-Semantik, SearchRun-Zählern, Observation-Zuordnung, Scheduling, CORS, Ports, Compose, CI und Teststack geschlossen sind. Berichte ausschließlich: - verbleibende echte fachliche Widersprüche, - noch fehlende Verträge, - technisch riskante Annahmen, - notwendige Backend-Änderungen, - notwendige Frontend-Änderungen. Keine neue Fachlogik erfinden, um Lücken still zu schließen. Wenn keine blockierenden Punkte mehr bestehen: 1. erstelle einen aktualisierten Implementierungsplan, 2. plane **vertikal** statt „Backend komplett, danach Frontend“, 3. jeder Slice muss unmittelbar über die UI testbar sein, 4. beginne die Implementierung erst nach ausdrücklicher Freigabe dieses Reviews.