6 plans (INGEST-02/03/07, SCHEMA-03) + Nyquist validation. Core (registry, fingerprint, dedup, TenderSource, backfill) lands first and is green independent of live scraping (D-01). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.7 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13-scraping-adapters-cross-source-dedup | 01 | execute | 1 |
|
true |
|
|
Purpose: Der Kern muss ohne jede Scraping-Abhaengigkeit voll getestet gruen sein (D-01). Dieser Plan ist genau dieser dependency-freie Datenkern. Output: schema.prisma (TenderSource + fingerprint), lokal angewendete Migration + Backfill, tender-fingerprint.ts (+ spec), erweiterte SourceType-Union.
<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/13-scraping-adapters-cross-source-dedup/13-CONTEXT.md @.planning/phases/13-scraping-adapters-cross-source-dedup/13-RESEARCH.md @apps/api/prisma/schema.prisma @apps/api/src/tenders/tender.types.ts @apps/api/src/tenders/tender-normalizer.service.ts Task 1: TenderSource-Modell + fingerprint-Spalte + Migration + Backfill apps/api/prisma/schema.prisma, apps/api/prisma/migrations/, apps/api/src/tenders/backfill-tender-source.ts Ergaenze in `schema.prisma` (per SCHEMA-03 / D-03, Referenz-Snippet in 13-RESEARCH.md "TenderSource-Modell (Prisma)"): neues `model TenderSource` mit id (uuid), tenderId, sourcePortal, sourceNoticeId, ocid (nullable), sourceUrl (nullable), createdAt, Relation `tender Tender @relation(fields:[tenderId], references:[id], onDelete: Cascade)`, `@@unique([sourcePortal, sourceNoticeId])` und `@@index([tenderId])`. Ergaenze in `model Tender`: `fingerprint String?` (nullable, additiv), `sources TenderSource[]`, `@@index([fingerprint])`. `dedupKey @unique` bleibt UNVERAENDERT (SCHEMA-02-Upsert-Target, D-01 — kein Drop in dieser Phase).Erzeuge eine handgeschriebene Migration (Verzeichnis apps/api/prisma/migrations/20260723120000_add_tender_source/migration.sql) in ZWEI logischen Schritten in korrekter Reihenfolge (Pitfall 5): (a) CREATE TABLE "TenderSource" + ADD COLUMN "fingerprint" auf "Tender" + Indizes + Unique-Constraint; (b) Daten-Backfill INSERT INTO "TenderSource" (...) SELECT gen_random_uuid(), t.id, t."sourcePortal", t."sourceNoticeId", t.ocid, t."sourceUrl", now() FROM "Tender" t; (SQL-Skizze in 13-RESEARCH.md "Backfill-Migration"). Der Unique-Constraint wird NACH dem Backfill-Insert wirksam bzw. das Insert erzeugt keine Duplikate (DÖE-noticeIds sind live eindeutig).
Der fingerprint-Backfill der Bestands-Tender lebt NICHT in reinem SQL (Umlaut-/CPV-Normalisierung lebt im Code): schreibe ein einmaliges TS-Backfill-Script backfill-tender-source.ts, das alle Tender laedt, tenderFingerprint(...) (Task 2) aus title/buyerName/cpvDivisions/deadlineAt/estimatedValue berechnet und Tender.fingerprint per updateMany/Schleife setzt. estimatedValue ist Decimal? — in number/null konvertieren, bevor es an valueBucket geht.
Wende Migration lokal an (KEIN Docker-Deploy Testserver, MEMORY): docker compose exec -T db psql -U tessera -d tessera_dev -f - bzw. pnpm --filter @tessera/api exec prisma migrate deploy gegen die lokale DB; danach pnpm --filter @tessera/api exec prisma generate. DB-Name lokal verifizieren (tessera_dev laut Phase-12-Vorbild; Research nennt tessera) — vor dem Anwenden \l pruefen.
cd apps/api && npx prisma validate && npx prisma generate
model TenderSource + Tender.fingerprint + Tender.sources existieren; prisma validate gruen; Migration lokal angewendet; SELECT count(*) FROM "TenderSource" == count der Tender (manuelle DB-Pruefung, siehe 13-VALIDATION.md).
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| Migration -> DB | Schema-/Datenaenderung an Produktions-naher lokaler DB; Reihenfolge-Fehler kann Backfill brechen. |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-13-01-01 | Tampering | Backfill-Migration | medium | mitigate | 2-Schritt-Reihenfolge (Tabelle+Spalten vor Constraint/Backfill, Pitfall 5); lokal angewendet, kein Testserver-Deploy. |
| T-13-01-02 | Denial of Service | fingerprint-Backfill ueber 2851 Rows | low | accept | Einmaliges Script, Batch/Schleife; kein Laufzeit-Pfad. |
| T-13-01-03 | Info Disclosure | Decimal->number Konvertierung estimatedValue | low | mitigate | NULL bleibt NULL (valueBucket NULL-tolerant); keine Praezisionsannahme im Hash (nur Groessenordnung). |
| </threat_model> |
<success_criteria> TenderSource-Tabelle existiert und ist 1:1 mit Bestands-Tendern backfilled; fingerprint-Spalte existiert + backfilled; pure Fingerprint-Fn NULL-tolerant + kollisionsarm getestet; SourceType-Union offen. TrAegt SCHEMA-03 (Datenschicht). </success_criteria>
Create `.planning/phases/13-scraping-adapters-cross-source-dedup/13-01-SUMMARY.md` when done