BuyingAgent/.specs/buying-agent-frontend-mvp-spec-v0.3.md
Lars b276758ee1
All checks were successful
Deploy Development / deploy (push) Successful in 1m19s
Test Suite / pytest-backend (push) Successful in 31s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-checks (push) Successful in 28s
Stelle den Frontend-MVP und die v0.3-API auf develop bereit, damit Dev API und PWA zusammen deployen.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 13:04:59 +02:00

1709 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 = <uuid>
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 = <uuid>
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 = <uuid>
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.