# QVS-P2 — value_stream_imports: Apply-Anleitung (KAR-971)

**Was:** Legt die neue Tabelle `value_stream_imports` an (Audit-/Herkunfts-Record
je QAF→Wertstrom-Import) plus die Funktion `create_value_stream_from_qaf`
(SECURITY DEFINER RPC für die atomare Erstellung). RLS `_own`+`_admin` ab
Tag 1, exakt das KAR-799/KAR-890-Muster. **App-Code nutzt das noch nicht** —
Flag `qafValueStream` bleibt in allen 3 Profilen `false`. Editor-tauglich
(kein `CONCURRENTLY`, jedes `CREATE POLICY` hat ein `DROP POLICY IF EXISTS`
davor, re-run-sicher).

**Stand 2026-07-18 (Review-Fixes, PR #336 adversarial review, 10/10 CONFIRMED
gefixt):** `project_id` ist jetzt `ON DELETE SET NULL` (+ nullable) statt
`CASCADE` — der Import-Audit-Trail überlebt eine Projekt-Löschung, analog
`value_stream_maps.project_id`. `created_by` ist jetzt `NOT NULL`; die
`_own`-Policy prüft zusätzlich `created_by = auth.uid()` als Fallback für
verwaiste (project_id NULL) Zeilen. `p_import_id`-Kollisionen liefern jetzt
einen sauberen `import_id_conflict`-Fehler statt eines rohen
`unique_violation`. Details: Migrations-Kopfkommentar + PR-Body/CHANGELOG.md
im Repo.

**Reihenfolge:**
1. `migration.sql` im Supabase SQL-Editor ausführen (Kadi-v2-Projekt).
2. Direkt danach die Verifikations-Queries unten laufen lassen.
3. Nur bei Problemen: `rollback.sql` ausführen (löscht Tabelle + Funktion
   wieder, editor-tauglich, ebenfalls re-run-sicher — **löscht den kompletten
   Import-Audit-Trail**, `value_stream_maps`-Zeilen selbst bleiben erhalten).

**Verifikation nach dem Apply:**

```sql
-- Tabelle leer, existiert
SELECT count(*) FROM value_stream_imports;
-- erwartet: 0

-- Policy-Check: genau 2 Policies (_own, _admin), keine auth.uid() IS NOT NULL-Variante
SELECT polname, pg_get_expr(polqual, polrelid) AS using_expr
FROM pg_policy
WHERE polrelid = 'public.value_stream_imports'::regclass;
-- erwartet: value_stream_imports_own ((created_by = auth.uid()) OR (project_id IN ...)),
--           value_stream_imports_admin (current_user_role() ...)

-- Policy-Namen + Command-Scope (Muster aus dem Review-Fix-Auftrag) —
-- bestätigt, dass beide Policies FOR ALL gelten (kein per-Command-Loch)
SELECT policyname, cmd FROM pg_policies WHERE tablename = 'value_stream_imports';
-- erwartet: value_stream_imports_own | ALL
--           value_stream_imports_admin | ALL

-- project_id/created_by Nullability-Check (Review-Fix 1)
SELECT column_name, is_nullable FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'value_stream_imports'
  AND column_name IN ('project_id', 'created_by');
-- erwartet: project_id | YES, created_by | NO

-- RPC vorhanden, SECURITY DEFINER
SELECT proname, prosecdef FROM pg_proc WHERE proname = 'create_value_stream_from_qaf';
-- erwartet: 1 Zeile, prosecdef = true
```

Lokal gegen echtes PostgreSQL 17 verifiziert (Migration + Re-Run + Rollback +
Rollback-Re-Run + Re-Apply-nach-Rollback, RLS-Isolation inkl. Waisen-
Sichtbarkeit, RPC-Transaktion inkl. Idempotenz, Fremdzugriffs-Ablehnung,
`p_import_id`-Kollision und Projekt-Löschungs-Überleben) — siehe
PR-Beschreibung/CHANGELOG.md für Details.

**Destruktives SQL zur Kenntnis:** `rollback.sql` enthält `DROP TABLE public.value_stream_imports`
(löscht die gesamte Audit-Tabelle inkl. aller Zeilen) und `DROP FUNCTION
public.create_value_stream_from_qaf(...)`. Nur ausführen, wenn der Apply
tatsächlich zurückgerollt werden soll — nicht Teil des Normalbetriebs.
