BuyingAgent/.specs/buying-agent-source-discovery-spec-v0.3.md
Lars d784248e28
Some checks failed
Deploy Development / deploy (push) Successful in 55s
Test Suite / pytest-backend (push) Failing after 30s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-checks (push) Successful in 26s
Schliesse das Notebook-Source-Mapping, damit Wizard und Taxonomie ohne leere Kategorie starten.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 14:17:56 +02:00

1304 lines
26 KiB
Markdown
Raw Permalink 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 – Source & Discovery 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-source-discovery-spec-v0.3.md`
**Gilt zusammen mit:**
1. `buying-agent-v0.1-spec-v1.1.md`
2. `buying-agent-architecture-addendum-v1.md`
3. `buying-agent-frontend-mvp-spec-v0.3.md`
Bei Widersprüchen innerhalb des Source-/Discovery-Bereichs hat diese v0.3 Vorrang.
---
# 1. Ziel
Diese Spezifikation definiert den Source-/Discovery-Pfad des **funktionierenden MVP**.
Sie ist ausdrücklich **kein Skeleton und kein theoretischer POC**.
Der MVP gilt in diesem Bereich erst dann als funktionsfähig, wenn der reale Pfad:
```text
Produktkategorie wählen
↓
SourceCategory bestimmen
↓
Geizhals-Taxonomie technisch lernen
↓
Filter und URL-Konstrukte verifizieren
↓
Kriterien über API/UI anbieten
↓
Such-URL aus ProjectCriteria erzeugen
↓
SearchRun ausführen
↓
Ergebnisse und Attribute parsen
↓
Produkte identifizieren / historisieren
↓
Diffing
↓
Kandidaten / Ranking / Shortlist
↓
UI
```
end-to-end funktioniert.
Initiale Source: **Geizhals**
Pilotkategorie: **Notebooks**
Die Architektur darf nicht notebook-spezifisch sein.
---
# 2. Grundprinzipien
1. Geizhals ist Discovery-Quelle, nicht Decision Engine.
2. Source-Kriterien bilden für unterstützte Kategorien zunächst den primären Kriterienkatalog.
3. Es gibt keine separate Canonical-Criteria-Schicht.
4. Jedes gelernte Source-Kriterium erhält eine stabile interne `CriterionDefinition.id` (UUID).
5. SearchFilter und Ergebnisattribute werden auf dieselbe `CriterionDefinition.id` gemappt.
6. INTERNAL-Kriterien werden nur ergänzt, wenn die Source relevante Eigenschaften nicht kennt.
7. URL-Encoding wird aus beobachtetem Source-Verhalten gelernt, nicht erfunden.
8. Nur verifizierte Mappings werden produktiv verwendet.
9. Taxonomy Learning und normale SearchRuns sind getrennte Prozesse.
10. Produktidentität bleibt konservativ.
11. Historie wird strukturiert gespeichert, kein HTML-Archiv.
12. Discovery, Ranking und Decision bleiben getrennt.
13. Vollständige Reconciliation darf nur auf vollständigen SearchRuns beruhen.
## 2.1 Konkret beobachtete Geizhals-Mechanismen für den Notebook-Pilot
Die aktuelle Notebook-Kategorie zeigt bereits ausreichend konkrete Muster, um den MVP-Pfad präziser festzulegen:
- zahlreiche Filter werden als auswählbare Optionen/Links dargestellt,
- Filtergruppen umfassen u. a. Hersteller, Diagonale, Auflösung, CPU-Hersteller, CPU-Serie, RAM, Speicher, Grafik, Betriebssystem und Gewicht,
- Preis, Gewicht und Diagonale sind typische Kandidaten für Range-/Input-Verhalten,
- die Ergebnisliste enthält bereits einen wesentlichen Teil der für den MVP relevanten Produktdaten, darunter Betriebssystem, Display, CPU, RAM, Speicher, Grafik, Modell-/Produktreferenz, Angebote und Bestpreis.
Diese Beobachtungen dienen als Pilotannahmen und müssen vor finaler Parser-Implementierung am echten Live-DOM validiert werden.
---
# 3. ProductCategory und SourceCategory
## 3.1 ProductCategory
Interner stabiler Katalog:
```text
ProductCategory
id
name
status
```
Beispiel:
```text
id = notebook
name = Notebooks
```
## 3.2 SourceCategory
Mapping zur Source:
```text
SourceCategory
id
source
external_category_key
product_category_id
source_url
status
taxonomy_status
last_taxonomy_scan_at
last_verified_at
```
Beispiel:
```text
source = geizhals
external_category_key = nb
product_category_id = notebook
```
---
# 4. SourceCategory Readiness
Eine SourceCategory besitzt einen technischen Readiness-Status:
```text
NOT_LEARNED
LEARNING
READY
DEGRADED
BROKEN
BLOCKED
```
## 4.1 READY
Eine SourceCategory ist `READY`, wenn mindestens:
- Baseline-Kategorieseite erreichbar ist
- Taxonomie technisch gelesen werden kann
- relevante Filterdefinitionen persistiert sind
- verwendbare URL-Encoding-Regeln verifiziert sind
- Query-Generation erfolgreich getestet wurde
- Result-Parsing mindestens für Basisdaten funktioniert
- kein aktiver Block-/Captcha-Zustand vorliegt
## 4.2 DEGRADED
`DEGRADED`, wenn:
- ein Teil der Filter nicht verifizierbar ist
- einzelne Mappings BROKEN sind
- Result-Parsing teilweise funktioniert
- Kategorie grundsätzlich weiter nutzbar ist
## 4.3 BROKEN
`BROKEN`, wenn Kernfunktionen nicht mehr zuverlässig arbeiten, z. B.:
- DOM-Struktur vollständig inkompatibel
- keine Query-Generierung mehr möglich
- Result Parser liefert keine verwertbaren Produkte
## 4.4 BLOCKED
`BLOCKED`, wenn Sourcezugriff aktuell durch:
- 403
- Captcha
- Challenge
- wiederholte 429
nicht weitergeführt werden soll.
---
# 5. Taxonomy Learning – operativer Algorithmus
Taxonomie wird **on demand** gelernt.
Der technische Ablauf ist verbindlich.
## 5.1 Schritt 1 – Baseline laden
Lade die Baseline-Kategorieseite, z. B.:
```text
https://geizhals.de/?cat=nb
```
Speichere:
```text
source_category
baseline_url
http_status
loaded_at
```
Keine HTML-Archivierung als Produktfunktion.
Fixtures dürfen für Tests aufgezeichnet werden.
## 5.2 Schritt 2 – Filtergruppen und Controls erkennen
Der Geizhals-Adapter analysiert die DOM-Struktur.
Zu erkennen sind, soweit vorhanden:
- Filtergruppen
- Filterlabel
- Checkboxen
- Radio-Optionen
- Dropdowns
- Multi-Selects
- Min/Max-Felder
- Range-Eingaben
- freie Texteingaben
- Links / hrefs
- optionale source-interne Filter-IDs
- Einheiten
- Wertebereiche
- sichtbare Optionslabels
Primärziel ist nicht die visuelle Darstellung, sondern die strukturelle Filtersemantik.
## 5.3 Schritt 3 – Filtermechanismus klassifizieren
Jeder erkannte Filter wird genau einem primären Lernweg zugeordnet:
### Typ A – Link-/href-basiert
Bevorzugter Weg.
Wenn Geizhals für eine Filteroption einen nutzbaren Link / href bereitstellt, wird dieser direkt ausgewertet.
Beispiel konzeptionell:
```html
<a href="?cat=nb&xf=17023_ab+24GB">ab 24 GB</a>
```
Der Adapter extrahiert:
```text
label
option_label
href
```
### Typ B – Range-/Input-Control
Für Controls wie Preis, Gewicht oder Diagonale, bei denen der Nutzer Werte eingibt oder einen Bereich setzt:
1. Baseline-URL erfassen.
2. einen kontrollierten Testwert setzen,
3. resultierende Navigation/Query-Änderung erfassen,
4. Query-Diff bestimmen,
5. Parametermuster persistieren,
6. mit einem zweiten Testwert prüfen, ob das Muster reproduzierbar ist.
### Typ C – JS-only Control
Nur wenn weder href noch statisch erkennbare Parameterstruktur vorhanden sind:
```text
Playwright-Fallback
```
Der Browser setzt den Filterzustand und beobachtet die resultierende URL bzw. Query.
Damit gilt die Priorität:
```text
A: href/link
→ B: Range/Input
→ C: Playwright-Fallback
```
## 5.4 Schritt 4 – Query-Diff bestimmen
Vergleiche Baseline-Query und Filter-Query.
Beispiel:
Baseline:
```text
?cat=nb
```
Filterzustand:
```text
?cat=nb&xf=17023_ab+24GB
```
Query-Diff:
```text
parameter = xf
value = 17023_ab+24GB
```
Für Preis können z. B. getrennte Parameter wie `bpmin` / `bpmax` erkannt werden.
## 5.5 Schritt 5 – Filterdefinition speichern
Erzeuge / aktualisiere:
```text
SourceFilterDefinition
```
Mindestens:
```text
id
source_category_id
criterion_definition_id
external_filter_key nullable
label
filter_type
unit nullable
encoding_type
status
last_seen_at
last_verified_at
```
Filtertypen:
```text
BOOLEAN
ENUM
MULTI_ENUM
NUMERIC_RANGE
NUMERIC_THRESHOLD
TEXT
UNKNOWN
```
Encoding-Typen mindestens:
```text
OPTION_TOKEN
RANGE_PARAMETERS
BOOLEAN_PARAMETER
TEXT_PARAMETER
UNKNOWN
```
---
# 6. SourceFilterOption / SourceFilterRange
## 6.1 Diskrete Optionen
```text
SourceFilterOption
- id
- source_filter_definition_id
- source_label
- normalized_value nullable
- operator nullable
- unit nullable
- query_parameter
- query_value
- status
```
## 6.2 Range-Filter
```text
SourceFilterRange
- source_filter_definition_id
- min_parameter nullable
- max_parameter nullable
- minimum nullable
- maximum nullable
- step nullable
- unit nullable
```
---
# 7. Playwright-Fallback für JS-only Controls
Wenn ein Filter kein direkt verwertbares href bzw. keine statisch erkennbare Query-Struktur liefert:
```text
Taxonomy Learning
↓
Playwright
```
Ablauf:
1. Kategorieseite öffnen
2. Ziel-Filter lokalisieren
3. Filterwert setzen
4. Navigation / URL-Änderung abwarten
5. resultierende URL erfassen
6. Query-Diff zur Baseline berechnen
7. Filterzustand wieder zurücksetzen
Wichtig:
- Playwright ist Fallback, nicht Primärmechanismus.
- Playwright wird für Taxonomy Learning verwendet, nicht für jeden normalen SearchRun.
- Kein Captcha-Bypass.
- Kein Login-Bypass.
- Kein Umgehen technischer Schutzmechanismen.
---
# 8. Mapping Lifecycle
Filter-/Encoding-Mappings besitzen Status:
```text
DISCOVERED
LEARNED_UNVERIFIED
VERIFIED
BROKEN
DISABLED
```
Nur `VERIFIED` wird für produktive Query-Generierung verwendet.
---
# 9. Roundtrip-Verifikation
Die Roundtrip-Verifikation ist verbindlich.
Beispiel:
```text
RAM >= 24 GB
↓
gelernte Query-Regel
↓
URL selbst generieren
↓
Geizhals aufrufen
↓
aktiven Filterzustand prüfen
```
Das Mapping wird nur `VERIFIED`, wenn der Adapter auf der Zielseite eindeutig erkennt, dass der erwartete Filter aktiv ist.
Wenn Verifikation fehlschlägt:
```text
LEARNED_UNVERIFIED
```
Wenn vorher VERIFIED und nun nicht mehr reproduzierbar:
```text
BROKEN
```
---
# 10. Erkennung von Source-Änderungen
Taxonomy Learning vergleicht:
```text
vorheriger Filterbestand
vs.
aktueller Filterbestand
```
Zu erkennen sind mindestens:
- Filter neu
- Filter verschwunden
- Filterlabel geändert
- externe Filter-ID geändert
- Optionen geändert
- Encoding geändert
- Unit geändert
- Control-Typ geändert
Bei relevanter Änderung:
```text
mapping_version++
```
Betroffene DiscoverySearches werden:
```text
OUT_OF_SYNC
```
wenn ihre Query auf geänderten Mappings basiert.
---
# 11. CriterionDefinition
Jedes Source-Kriterium erhält eine stabile interne ID:
```text
CriterionDefinition
- id UUID
- origin SOURCE | INTERNAL
- product_category_id
- source_id nullable
- source_category_id nullable
- label
- value_type
- unit nullable
- scoring_attribute_key nullable
- status
```
`CriterionDefinition.id` ist Referenzidentität, keine globale Ontologie.
---
# 12. SourceAttributeMapping
Suchfilter und Produktattribute müssen verbunden werden.
Dafür:
```text
SourceAttributeMapping
- id
- source_category_id
- source_attribute_key nullable
- source_attribute_label
- criterion_definition_id
- normalizer_id nullable
- status
```
Matching-Priorität:
1. stabile Source-Attribute-ID
2. bekannte SourceAttributeMapping-Regel
3. normalisiertes Label-Matching
4. ansonsten UNMAPPED
Keine aggressive automatische Zuordnung bei Unsicherheit.
---
# 13. available-criteria
Endpoint:
```text
GET /api/v1/product-categories/{product_category_id}/available-criteria?source=geizhals
```
Nur verwendbare Kriterien werden dem Standardnutzer als Discovery-Filter angeboten.
Mindestens:
```text
criterion_definition_id
label
filter_type
unit
operators
options / range
mapping_status
supports_discovery
```
---
# 14. Query Generation
Die Source-URL wird ausschließlich aus:
```text
SourceCategory
+
aktuelle ProjectCriteria
+
VERIFIED SourceFilterMappings
```
erzeugt.
Keine URL-Templates aus LLM.
Keine hartcodierten notebook-spezifischen Query-Fragmente außerhalb des Geizhals-Adapters / Source-Datenmodells.
Die Query-Generierung muss deterministisch, reproduzierbar, testbar und versionsgebunden sein.
---
# 15. SearchRun
Ein SearchRun hält mindestens:
```text
discovery_search_id
criteria_version
mapping_version
generated_url
started_at
finished_at
status
result_count
page_count
new_count
changed_count
unchanged_count
missing_count
```
---
# 16. Ergebnisabruf und Pagination
Ein Full SearchRun versucht grundsätzlich die vollständige Ergebnismenge zu lesen.
Wenn nicht alle Seiten vollständig gelesen werden:
```text
SearchRun.status = PARTIAL
```
Ein `PARTIAL`-Run darf keine:
```text
MISSING
DISAPPEARED
```
Transitions erzeugen.
---
# 17. TOO_BROAD
Eine DiscoverySearch darf einen konfigurierbaren Schwellenwert besitzen.
Beispiel:
```text
broad_result_threshold = 300
```
Bei Überschreitung:
```text
SearchRun.status = TOO_BROAD
```
Kein stilles Top-N-Limit.
Ein technischer Safety-Cap ist zulässig, wenn er explizit konfiguriert und sichtbar dokumentiert ist; der Lauf gilt dann nicht als Full Run.
---
# 18. Zweistufige Datenerfassung
Die Suchergebnisliste ist die **primäre Datenquelle des MVP**.
Der Notebook-Pilot muss zunächst möglichst viel aus der Ergebnisliste gewinnen, bevor zusätzliche Produktdetailseiten geladen werden.
## 18.1 Stufe A – Suchergebnisliste
Jeder SearchRun liest aus der Ergebnisliste mindestens:
```text
source_product_url
title
manufacturer/model soweit sichtbar
best_price
currency
availability soweit sichtbar
seller_count soweit sichtbar
sichtbare Attribute
```
Für die Notebook-Kategorie sind insbesondere folgende sichtbare Basisattribute als realistische MVP-Felder zu validieren:
```text
Betriebssystem
Display
CPU
RAM
Speicher
Grafik
Modell-/Produktreferenz
Angebote
Bestpreis
```
Die genaue DOM-Struktur und die Verfügbarkeit einzelner Felder dürfen nicht vorab erfunden werden; sie werden im technischen Spike und Live-Pilot bestätigt.
## 18.2 Stufe B – Produktdetailseite
Produktdetailseiten werden nicht blind für alle Treffer geladen.
Sie werden für relevante Kandidaten geladen, insbesondere:
- Shortlist
- Top-N nach ProductScore
- Kandidaten mit fehlenden für Ranking relevanten Attributen
- Kandidaten mit unsicherer Identität, wenn Detailseite helfen kann
Der Notebook-Pilot muss praktisch feststellen, welche Daten auf Liste und Detailseite verfügbar sind.
---
# 19. SourceProductReference
Mindestens:
```text
id
source_id
source_category_id
source_url
source_title
manufacturer nullable
model nullable
manufacturer_sku nullable
gtin_ean nullable
normalized_spec_fingerprint nullable
match_status
lifecycle_status
first_seen_at
last_seen_at
```
---
# 20. Produktidentität – konservativer Pilot
Signalreihenfolge:
1. stabile Source-ID, falls entdeckt
2. GTIN/EAN
3. Hersteller-SKU
4. kanonische Source-URL
5. normalisierter Titel
6. Hersteller + Modell
7. differenzierende Specs
Bei Unsicherheit:
```text
MATCH_UNCERTAIN
```
Kein automatisches Merge.
---
# 21. ProductObservation
```text
ProductObservation
- id
- source_product_reference_id
- search_run_id
- observed_at
- raw_title
- best_price
- currency
- availability nullable
- seller_count nullable
```
---
# 22. ProductAttributeObservation
```text
ProductAttributeObservation
- product_observation_id
- criterion_definition_id nullable
- raw_name
- raw_value
- normalized_value nullable
- unit nullable
- mapping_status
```
Wenn kein Mapping existiert:
```text
criterion_definition_id = null
mapping_status = UNMAPPED
```
Unmapped Felder dürfen für spätere Admin-Korrektur sichtbar bleiben.
---
# 23. Normalisierung
Normalizer arbeitet kriteriumsabhängig.
Beispiele:
```text
"32 GB" -> 32 / GB
"1 TB" -> 1024 / GB, falls Zielunit GB
"1,45 kg" -> 1.45 / kg
```
Zu speichern:
```text
raw_value
normalized_value
unit
normalizer_version
```
---
# 24. Diffing
Verglichen werden nur vollständige Runs derselben DiscoverySearch.
Run Diff:
```text
NEW
CHANGED
UNCHANGED
MISSING
```
CHANGED mindestens bei relevanter Änderung von:
```text
best_price
currency
availability
seller_count
normalisierte Attribute
```
---
# 25. Product Lifecycle
```text
ACTIVE
DISAPPEARED
ARCHIVED
```
Regeln:
```text
1 Full Run fehlend -> Run Diff MISSING
3 aufeinanderfolgende Full Runs fehlend -> Lifecycle DISAPPEARED
```
---
# 26. Ranking und Shortlist
Bestehende Scoring-Engine kann über:
```text
CriterionDefinition.scoring_attribute_key
```
angesprochen werden.
Ohne Ranking-Konfiguration:
```text
ranking_status = NOT_CONFIGURED
```
Mit Ranking:
```text
ProductScore
product_eligibility
```
Shortlist Default: `5`
---
# 27. Scheduling und Backoff
Default:
```text
180 Minuten
```
auf `DiscoverySearch`.
Defensives Verhalten:
- Timeout
- Retry mit exponentiellem Backoff
- Jitter
- 429 -> BACKOFF
- 403 -> BLOCKED
- Captcha / Challenge -> BLOCKED
- wiederholte Parsing-Fehler -> DEGRADED/BROKEN
Keine Schutzumgehung.
---
# 28. UI-Reaktion auf SourceCategory Readiness
## READY
Normale Kriterienauswahl und Suchanlage.
## NOT_LEARNED
UI zeigt: „Kriterien für diese Produktkategorie werden gerade vorbereitet.“
## LEARNING
Loading-/Progress-Zustand.
## DEGRADED
VERIFIED Kriterien dürfen angeboten werden; Warnung anzeigen.
## BROKEN
Keine neue produktive Suche.
## BLOCKED
Quelle derzeit nicht automatisiert abfragbar.
---
# 29. Taxonomy Learning API
Mindestens:
```text
POST /api/v1/sources/{source}/categories/{category}/discover-taxonomy
GET /api/v1/sources/{source}/categories/{category}/taxonomy-status
GET /api/v1/sources/{source}/categories/{category}/filters
```
Response mindestens:
```text
source_category_status
filters_discovered
filters_verified
filters_unverified
filters_broken
mapping_version
last_scan_at
```
---
# 30. Source Adapter Contract
```python
class DiscoverySourceAdapter(Protocol):
def load_category_baseline(category_ref): ...
def discover_filter_controls(document): ...
def extract_filter_links(document): ...
def derive_query_diff(baseline_url, filtered_url): ...
def verify_filter_roundtrip(filter_definition, encoded_query): ...
def build_query(criteria, mappings): ...
def fetch_result_page(url): ...
def parse_result_page(document): ...
def parse_product_detail(document): ...
def extract_active_filters(document): ...
def normalize_source_attribute(raw_attribute): ...
```
Playwright darf intern als Fallback-Service genutzt werden.
---
# 31. Technische Module
Mindestens logisch getrennt:
```text
sources/geizhals/
category_loader
taxonomy_parser
filter_encoder
roundtrip_verifier
result_parser
product_detail_parser
discovery/
taxonomy_service
criterion_service
mapping_service
search_execution_service
identity_service
observation_service
diff_service
candidate_service
shortlist_service
```
Keine Geizhals-DOM-Logik außerhalb `sources/geizhals`.
---
# 32. Verpflichtender technischer Geizhals-Spike vor finalem Parserbau
Vor der finalen Implementierung der Geizhals-Parser muss der Coding-Agent die echte Notebook-Seite technisch inspizieren.
Der Spike muss dokumentieren:
1. stabile DOM-Anker bzw. semantisch belastbare Selektoren,
2. tatsächliche Struktur der Filtergruppen,
3. Klassifikation der Filter in Typ A/B/C,
4. konkrete href-/Query-Muster mehrerer Filter,
5. tatsächliches Verhalten von Preis, Gewicht und Diagonale,
6. Pagination-Struktur,
7. Produktkarten-/Ergebnisstruktur,
8. verfügbare Felder direkt in der Ergebnisliste,
9. Unterschiede zwischen Listen- und Produktdetaildaten,
10. auffällige dynamische/JS-geladene Bereiche.
Wichtig:
- keine CSS-Klassen, DOM-Pfade oder Selektoren vorab erfinden,
- bevorzugt stabile semantische Attribute oder Source-eigene IDs verwenden,
- fragile rein visuelle Selektoren nur, wenn keine robustere Alternative existiert,
- die Ergebnisse des Spikes müssen unmittelbar in Fixtures und Parser-Tests überführt werden.
Der Spike ist Teil der MVP-Umsetzung, nicht optionale Vorarbeit.
---
# 34. Fixtures
Fixtures sind für reproduzierbare Tests Pflicht.
Mindestens:
- Notebook Baseline Category HTML
- Kategorie mit gesetztem RAM-Filter
- Kategorie mit Preisbereich
- Ergebnisliste Seite 1
- Ergebnisliste Seite 2
- Produktdetailseite
- geänderte DOM-Version / broken fixture
- Captcha/Challenge fixture, soweit technisch sinnvoll
- 429/403 als HTTP-Testzustände
---
# 34. Unit Tests
Mindestens:
## Taxonomy
- Filtergruppen erkannt
- Filterlabel erkannt
- href extrahiert
- Query-Diff korrekt
- Filteroption gespeichert
- Range-Parameter erkannt
- JS-only Control nutzt Fallback-Pfad
- Mapping Lifecycle korrekt
- Roundtrip -> VERIFIED
- Roundtrip fail -> LEARNED_UNVERIFIED
- ehemals VERIFIED + fail -> BROKEN
## Attribute Mapping
- RAM -> richtige CriterionDefinition
- unbekanntes Label -> UNMAPPED
- Source-ID hat Vorrang vor Label
- Unit-Normalisierung korrekt
## Query Generation
- nur VERIFIED
- deterministisch
- criteria_version/mapping_version gebunden
- INTERNAL ohne Source-Mapping beeinflusst URL nicht
---
# 35. Integration Tests
Mindestens:
```text
Taxonomy Fixture
→ DB
→ available-criteria
→ ProjectCriteria
→ Query Generation
→ SearchRun Fixture
→ Result Parse
→ SourceProductReference
→ ProductObservation
→ ProductAttributeObservation
→ Diff
→ Candidate DTO
```
---
# 36. Manueller Live-Notebook-Pilot
Der MVP gilt im Source-/Discovery-Bereich **nicht als abgenommen**, solange kein dokumentierter Live-Pilot gegen die echte Geizhals-Notebook-Kategorie erfolgreich durchgeführt wurde.
Der Pilot muss mindestens zeigen:
1. Notebook-Kategorie wird erreichbar geladen.
2. reale Filter werden erkannt.
3. mehrere unterschiedliche Filtertypen werden gelernt.
4. RAM-Filter wird technisch korrekt gelernt.
5. Preisfilter wird technisch korrekt gelernt.
6. Roundtrip-Verifikation funktioniert.
7. `available-criteria` liefert reale Filter.
8. daraus wird eine reale Such-URL generiert.
9. reale Suchergebnisse werden gelesen.
10. mindestens Titel + Produktlink + Preis werden extrahiert.
11. sichtbare Produktattribute werden gemappt.
12. mindestens zwei SearchRuns erzeugen History/Diff.
13. UI kann daraus Kandidaten anzeigen.
Der Live-Pilot muss mindestens einen vollständigen technischen Kettennachweis enthalten:
```text
echte Filtergruppe erkannt
→ href/Input-Verhalten ermittelt
→ Query-Regel gelernt
→ Roundtrip verifiziert
→ reale Ergebnisse geparst
→ reale Attribute auf CriterionDefinition gemappt
```
Live-Pilot wird manuell/dokumentiert ausgeführt, nicht in CI.
---
# 37. Fehlerfälle
## DOM geändert
`DEGRADED` oder `BROKEN`
## Filter nicht verifizierbar
`LEARNED_UNVERIFIED`
## URL-Encoding geändert
früher VERIFIED, Roundtrip scheitert:
```text
BROKEN
mapping_version++
DiscoverySearch -> OUT_OF_SYNC
```
## Result Parser unvollständig
teilweise Daten -> `DEGRADED`
keine verwertbaren Produkte -> `BROKEN`
## Source blockiert
`BLOCKED`
## Taxonomie teilweise verfügbar
VERIFIED Kriterien dürfen weiter angeboten werden.
---
# 38. Kein stiller Fallback
Nicht erlaubt:
- Filter-Encoding raten
- unbekannte Source-Attribute automatisch mergen
- unvollständigen Run als vollständigen Run behandeln
- BROKEN Mapping weiterverwenden
- fehlende Kriterien durch frei erfundene Defaults ersetzen
- Candidate Ranking mit nicht belegten Attributen auffüllen
---
# 39. Definition of Done
## Taxonomy / URL Learning
- [ ] Kategorieseite wird real gelesen.
- [ ] Filter werden aus dem DOM erkannt.
- [ ] href-basierte Filter werden automatisch gelernt.
- [ ] Query-Diff funktioniert.
- [ ] Range-Parameter funktionieren.
- [ ] Playwright-Fallback existiert für JS-only Controls.
- [ ] Roundtrip-Verifikation existiert.
- [ ] Mapping Lifecycle funktioniert.
- [ ] Geizhals-Strukturänderungen werden erkannt.
- [ ] SourceCategory Readiness funktioniert.
## Criteria / Mapping
- [ ] Source-Kriterien erhalten stabile CriterionDefinition-UUIDs.
- [ ] SearchFilter referenziert CriterionDefinition.
- [ ] SourceAttributeMapping referenziert dieselbe CriterionDefinition.
- [ ] UNMAPPED Attribute bleiben sichtbar.
- [ ] INTERNAL-Kriterien möglich.
- [ ] available-criteria basiert auf real gelernter Taxonomie.
## Search / Results
- [ ] Query-Generierung nutzt nur VERIFIED Mappings.
- [ ] Full Pagination funktioniert.
- [ ] PARTIAL wird korrekt erkannt.
- [ ] PARTIAL löst kein MISSING/DISAPPEARED aus.
- [ ] Basisdaten aus Ergebnisliste werden extrahiert.
- [ ] Produktdetailseite kann gezielt angereichert werden.
- [ ] SourceProductReference funktioniert.
- [ ] ProductObservation funktioniert.
- [ ] ProductAttributeObservation funktioniert.
- [ ] Attribute werden normalisiert/historisiert.
## Diff / Candidate
- [ ] NEW / CHANGED / UNCHANGED / MISSING funktionieren.
- [ ] DISAPPEARED nach drei Full Runs.
- [ ] Candidate DTOs werden erzeugt.
- [ ] Ranking nutzt nur vorhandene gemappte Daten.
- [ ] Shortlist funktioniert, wenn Ranking konfiguriert.
## MVP Acceptance
- [ ] Notebook Live-Pilot vollständig dokumentiert.
- [ ] echte Geizhals-Kriterien im UI sichtbar.
- [ ] echte Such-URL aus UI-Kriterien generiert.
- [ ] echte Resultate eingelesen.
- [ ] echte Preise/Attribute angezeigt.
- [ ] History/Diff über mindestens zwei Runs sichtbar.
- [ ] Source-/Discovery-Pfad end-to-end über UI nutzbar.
---
# 40. Arbeitsauftrag an den Coding-Agenten
Diese Datei ersetzt `buying-agent-source-discovery-spec-v0.2.md` vollständig.
Bitte **noch nicht implementieren**.
Prüfe erneut den aktuellen Repository-Stand gegen:
1. `buying-agent-v0.1-spec-v1.1.md`
2. `buying-agent-architecture-addendum-v1.md`
3. `buying-agent-source-discovery-spec-v0.3.md`
4. `buying-agent-frontend-mvp-spec-v0.3.md`
Prüfe insbesondere:
- ob der Taxonomy-Learning-Algorithmus vollständig implementierbar beschrieben ist,
- ob DOM/href-basierte Discovery ausreicht bzw. welche konkreten Geizhals-Strukturen im Live-Pilot validiert werden müssen,
- ob Playwright-Fallback sauber begrenzt ist,
- ob Query-Diff + Roundtrip-Verifikation widerspruchsfrei sind,
- ob SourceFilterMapping und SourceAttributeMapping klar getrennt sind,
- ob dieselbe CriterionDefinition-ID Search und Result Attributes verbindet,
- ob Readiness-Status ausreichend definiert sind,
- ob zweistufige Datenerfassung (Liste/Detailseite) implementierbar ist,
- ob Full/PARTIAL-Semantik vollständig ist,
- ob der Notebook Live-Pilot als echte MVP-Abnahme genügt,
- ob der technische Geizhals-Spike vor finalem Parserbau ausreichend konkret beschrieben ist,
- ob stabile DOM-Anker und Pagination im Live-DOM ohne erfundene Selektoren verifiziert werden können,
- ob Ergebnislisten-Daten für den MVP tatsächlich ausreichen und welche Felder zwingend Detailseiten benötigen.
Berichte ausschließlich:
- verbleibende echte fachliche Widersprüche,
- fehlende technische Verträge,
- technisch riskante Annahmen,
- notwendige Backend-Änderungen,
- notwendige Frontend-Änderungen.
Keine neue Fachlogik erfinden.
Wenn keine blockierenden Punkte mehr bestehen:
1. aktualisierten vertikalen Implementierungsplan erstellen,
2. jeder Slice muss über die UI prüfbar sein,
3. Taxonomy Learning + Query Generation + Result Parsing früh implementieren,
4. Live-Notebook-Pilot vor Abschluss durchführen,
5. Implementierung erst nach ausdrücklicher Freigabe des Reviews starten.