# QAF → Wertstrom: Zielarchitektur & Phasenplan

Stand: 2026-07-17 · Programm QVS · Voraussetzung: `qaf-value-stream-current-state.md`, `qaf-value-stream-gap-analysis.md`. „Spec N" = Abschnitte in `reports/qaf-value-stream-spec-source.txt`.

## 1. Leitentscheidungen

**E1 — Lesen aus der Haupt-Engine, nicht neu parsen.** Quelle der Prozessschritte ist `qaf_manufacturing_step` (relational, mit `raw_values/normalized/source_cells`) plus `qaf_file.g60_meta.capability_matrix`. Kein neuer Parser, kein Lesen aus `qaf_uploads.parsed_data` (Legacy-Pfad B bleibt unberührt).

**E2 — Additives VSM-Datenmodell, kein Bruch.** Alle neuen Node-Felder sind optional im bestehenden `nodes`-JSONB; bestehende Wertströme bleiben byte-kompatibel gültig. Es gibt KEINE Umstellung von Nodes auf relationale Tabellen (zu invasiv, kein Spec-Zwang: „Do not create unnecessary entities when existing models can be extended safely").

**E3 — Ein neues Audit-/Import-Objekt statt Dokument-Versionierung.** `value_stream_imports` (Tabelle) trägt Quelle, Hash, Versionen, Zählwerte, Warnungen und einen Import-Snapshot der erzeugten Nodes. Das deckt Spec 17/22/26 (Traceability, Duplikat-Schutz, Sync-Basis) mit minimalem Schema — volle VSM-Versionierung bleibt Follow-up (Gap G9).

**E4 — Projekt-Scope erzwungen.** Ein aus QAF erzeugter Wertstrom übernimmt zwingend die `project_id` der QAF-Quelle. Kein projektloser Import (RLS-Scope-Verlust für Kosten-Daten, Gap G12). Sichtbarkeit folgt damit exakt der bestehenden `vsm_own`-Policy + QAF-`_own`-Policy — wer die QAF-Analyse sehen darf, darf den daraus erzeugten Wertstrom sehen.

**E5 — Feature-Flag `qafValueStream`, überall `false` bis zur Validierung.** Muster wie Multi-QAF-Rollout (Runbook-Vorbild): Phasen mergen flag-off, Flag-ON als eigener Mini-PR nach Korpus-Validierung, Rollback = Ein-Zeilen-Revert.

**E6 — Fabrikations-Verbot operationalisiert.** Nur Felder mit real extrahiertem Wert werden in Nodes geschrieben; fehlende Werte bleiben `undefined` mit dokumentiertem Grund im Preview/Import-Record (`missingFieldReasons`). Varianten ohne Schritt-Ebene (Multi-QAF, Gap G5) erzeugen NIE synthetische Schritt-Zeiten.

**E7 — Ein ADR** (`docs/adr/`) hält E1–E6 fest (Repo-Regel: Architektur-Entscheidungen → ADR), kommt mit dem P1-PR.

## 2. Modul-Layout (ADR-019 Golden Path)

```
lib/qaf-value-stream/
  index.ts                      ← Barrel (Public API)
  README.md
  internal/
    types.ts                    ← QvsImportPreview, VsmNodeQafSource, ImportRecord, …
    manufacturing-capability.ts ← assessManufacturingCapability(qafFileId | parsed rows)
    mapper.ts                   ← mapQafRowsToVsmNodes(rows, opts) — pur, versioniert
    naming.ts                   ← buildDefaultTitle(supplier, project, variant)
    va-classification.ts        ← conservativeVaClass(processName) → va|nnva|nva|unknown
    preview.ts                  ← buildImportPreview(...)
    duplicates.ts               ← findExistingImports(fileHash, projectId, variant)
    creation.ts                 ← createValueStreamFromQaf(...) (transaktional)
    sync.ts                     ← (Phase P5) Quell-Delta + kontrollierte Übernahme
```

Server-Wiring als Server Actions in `app/qaf-differences/actions.ts`-Stil, aber im eigenen File `app/wertstrom/qaf-actions.ts` (hält den 4700-Zeilen-Koloss stabil; Import-Boundary: nur über `lib/qaf-value-stream` Barrel).

### Service-Verantwortungen

| Service | Input | Output | Kernregeln |
|---|---|---|---|
| `manufacturing-capability` | `qaf_file.id` | `{eligible, stepCount, fieldCoverage, warnings, variantContext}` | eligible = ≥1 Schritt mit Prozessname; KEIN G60-/INPUT-/Template-Zwang (nutzt vorhandene `qaf_manufacturing_step`-Zeilen egal aus welchem Sheet-Alias sie kamen) |
| `mapper` | `QAFRow[]` (rehydriert aus `raw_values`) | `VsmNode[]` + `VsmConnection[]` + per-Node-Lineage | Quell-Reihenfolge = `row_index` (Spec 7); lineare Verkettung; Einheiten-Regel s→s; `MAPPING_VERSION` Konstante; nie fabrizieren (E6) |
| `preview` | capability + mapper-Ergebnis + Duplikat-Check | `QvsImportPreview` | Abwählbare Schritte, editierbarer Titel, Coverage je Feldgruppe, `missingFieldReasons`, Duplikat-Hinweis |
| `creation` | bestätigtes Preview | `{valueStreamId, importId}` | EINE Transaktion: `value_stream_maps`-Insert + `value_stream_imports`-Insert; `project_id` aus Quelle (E4); Fehler → nichts persistiert (Spec 36) |
| `duplicates` | `file_hash, project_id, variant` | bestehende Imports/VSMs | nur Hinweis + Wahl (Öffnen/Neu), kein Block (Spec 22) |
| `va-classification` | Prozessname | `vaClass` | konservativ: Muster für transport/prüf/rework/lager → nva bzw. nnva; sonst `unknown`; NIE automatisch `va` (Bruch mit dem Excel-Import-Fehler `isValueAdded=true` hartcodiert) |

## 3. Datenmodell-Änderungen

### 3.1 `VsmNode` — additive optionale Felder (P1)

```ts
// Kosten (aus QAF, Anzeige-/Rollup-Basis; Einheiten wie Quelle, Währung explizit)
costPerUnit?: number          // fk (BW) — primäre Prozesskosten je Teil
machineHourRate?: number      // mss (BW/h — Stundensatz)
laborHourRate?: number        // lohnkosten (BW/h — Stundensatz, KEIN Pro-Stück-Wert; Review-Fix, vormals fälschlich laborCostPerUnit)
setupCostPerUnit?: number     // ruestkosten
scrapRate?: number            // ausschuss (%)
scrapCostPerUnit?: number     // ausschusskosten (AW — NICHT currency/BW! Review-Fix)
currency?: string             // beschaffungswaehrung (BW — gilt NICHT für scrapCostPerUnit, s.o.)
partsPerCycle?: number        // teileProZyklus
location?: string             // standort (Freitext, Spec-Beispiel „Production location")
// Klassifikation (ersetzt semantisch isValueAdded, ohne es zu brechen)
vaClass?: 'va' | 'nnva' | 'nva' | 'unknown'
// Varianten & Kategorie
variantTags?: string[]
// Quelle & Änderungs-Status
qafSource?: VsmNodeQafSource       // s. schemas/value-stream-process-source.schema.json
fieldStatus?: Partial<Record<VsmNodeFieldKey, 'imported' | 'modified'>>
```

Kompatibilitätsregeln: `isValueAdded` bleibt gelesen/geschrieben (Editor); bei Import wird `isValueAdded = (vaClass === 'va')` gesetzt, `vaClass` ist führend, Editor bekommt 4-Wege-Auswahl (P3). `fieldStatus` wird nur für importierte Nodes gepflegt (Editor-Setter markiert `modified`); manuell erzeugte Nodes tragen keins von beiden (Abwesenheit = manuell, kein UI-Rauschen — Spec 18).

`VsmNodeQafSource` (im Node-JSONB, kompakt): `{ importId, sheet, rowIndex, positionNumber, fields: { [feld]: { cell, original, unit?, confidence? } } }` — Original-Werte bleiben beim Editieren unangetastet (Spec 18: „Manual changes do not destroy original source values").

### 3.2 Neue Tabelle `value_stream_imports` (P2, Migration → Kais)

```
id uuid PK · value_stream_id uuid FK→value_stream_maps ON DELETE CASCADE
qaf_file_id uuid FK→qaf_file ON DELETE SET NULL · project_id uuid FK→projects ON DELETE SET NULL
source_file_name text · file_hash text · variant_selector jsonb
parser_version text · mapping_version text · engine_context jsonb
imported_step_count int · excluded_step_count int
warnings jsonb · missing_field_reasons jsonb
import_snapshot jsonb        ← Nodes+Connections wie erzeugt (Sync-Referenz, Spec 21)
manual_preview_corrections jsonb   ← Spec 26 (Korrekturen im Preview)
created_by uuid NOT NULL FK→auth.users · created_at timestamptz DEFAULT now()
```

**project_id (Review-Fix 1, 2026-07-18):** `ON DELETE SET NULL`, nicht `CASCADE` — analog `value_stream_maps.project_id` (ebenfalls SET NULL). Der Audit-/Herkunfts-Record muss eine Projekt-Löschung überleben, exakt wie `qaf_file_id`; ein Wertstrom, der die Löschung übersteht, darf seine Import-Historie nicht hart verlieren. `created_by` ist deshalb `NOT NULL` (RPC füllt es immer aus `auth.uid()`), und die `_own`-Policy prüft zusätzlich `created_by = auth.uid()` als Fallback, sobald `project_id` NULL wird.

RLS: etabliertes `_own`+`_admin`-Paar über `project_id IN (eigene projects) OR created_by = auth.uid()` — exakt das `qaf_*`-Muster (das historische Lax-Policy-Loch von `qaf_process_mappings`, seit KAR-890 geschlossen, bleibt Warnbeispiel) plus das `vsm_own`-Created-by-Fallback-Muster (`value_stream_maps`). Indizes: `value_stream_id`, `(project_id, file_hash)` für Duplikat-Check. Rollback-Skript im selben PR (Programm-Standard).

### 3.3 Bestandsschema unangetastet

`value_stream_maps` bekommt KEINE neuen Spalten (Verknüpfung läuft über `value_stream_imports.value_stream_id`; 1 VSM : N Imports über die Zeit = Re-Import-Historie gratis).

## 4. API / Server-Boundary

Server Actions (auth + Projekt-Scope via RLS, strukturierte Fehler):
- `getQafValueStreamCapability(qafFileId)` → Capability + Duplikat-Status (GET-Semantik)
- `buildQafValueStreamPreview(qafFileId, options)` → `QvsImportPreview`
- `createValueStreamFromQafAction(previewConfirmation)` → `{valueStreamId}` (idempotent über `previewToken`, transaktional)
- P5: `getValueStreamSyncDelta(valueStreamId)` / `applyValueStreamSyncSelection(...)`

Kein neuer REST-Pfad unter `app/api/` (Repo-Prinzip „API-First = Supabase/Actions, custom routes nur wenn nötig"); OpenAPI-Katalog unberührt bis ein externer Konsument entsteht.

## 5. UX-Flow (Detail: `qaf-value-stream-ux-flow.md`)

Entry-Points (P3, Flag-gated): (1) `qaf-differences/[id]`-Detail (summary/g60) neben Export-Buttons, (2) `project/[id]/qaf`. Aktion: **„Als Wertstrom übernehmen"**. Ein Preview-Dialog (ein Bestätigungs-Klick), danach Redirect in den Editor. Multi-QAF: Varianten-Sektion im Preview (geteilter Fluss + Tags, ehrliche Datenlage-Anzeige).

## 6. Kennzahlen (P4)

`vsm-metrics.ts` additiv erweitern: `computeTimeline` versteht `vaClass` (nnva separat), neue Rollups (setup/wait/transport-Summen, Kosten- und Scrap-Summe je Währung — bei Währungs-Mix getrennte Buckets, KEINE stille Summierung: Doktrin aus KAR-938), Takt-Vergleich bleibt an `projects.customer_takt_time_sec`. Jede neue Kennzahl bekommt Formel+Datenbasis+Ausschlüsse in einem Info-Panel (Explain-Muster). Graph-Traversal-Umbau bleibt draußen (Importe sind linear, Gap G6).

## 7. Phasenplan (je Phase 1 PR, etabliertes Gate-Muster)

| Phase | Inhalt | Migration? | Risiko |
|---|---|---|---|
| **P0** | `reports/` (current-state, gap, architecture, field-mapping, ux-flow) + `schemas/` + `mappings/` + CHANGELOG | nein | docs-only |
| **P1** | Modul-Scaffold + Flag `qafValueStream` (3 Profile, false) + ADR + `types/mapper/naming/va-classification/manufacturing-capability` PUR mit Tests + **Korpus-Evidenz-Scan** (Header-Präsenz Rüstzeit/Zeit-Split/Kapazität/Losgröße auf dev-Split → entscheidet G4-Scope) + Real-Korpus-Test des Mappers | nein | klein (pure Logik) |
| **P2** | Migration `value_stream_imports` (+Rollback, RLS-Tests) + `preview/duplicates/creation` + Server Actions | **ja → Kais** | mittel |
| **P3** | UI: Entry-Points, Preview-Dialog, Navigation, Lineage-Panel („Quelle anzeigen"), Import≠Manuell im Editor, vaClass-Auswahl; PRODUCT_SPEC/UI_FLOWS-Update (Feature-Parity-Regel) | nein | mittel |
| **P4** | Varianten-Handling (Multi-QAF-Preview, Tags) + Kennzahlen-Erweiterung + ggf. G4-Extraktionsfelder (nur mit P1-Evidenz; neue Canonical-Fields + Parser-Test) | nein | mittel |
| **P5** | Reimport/Sync (Delta-Berechnung gegen import_snapshot, Status-Modell, selektive Übernahme, „nie still überschreiben") | nein | mittel |
| **P6** | Korpus-Validierung (dev-Kalibrierung, validation-Metriken, Worst-Performer), Regression komplett, `test-results/validation-results/regression-results/unresolved-limitations` | nein | — |
| **P7** | Flag-ON-PR (nach P6-Evidenz) + finaler deutscher Bericht (Spec 39) + TG-Bilanz | nein | klein |

Abhängigkeiten: P1→P2→P3 strikt; P4/P5 nach P3; P6 nach P4; P7 zuletzt. Holdout-Split wird NICHT erneut angerührt (bereits verbraucht, 16.07.) — Validierungs-Design nutzt dev+validation und dokumentiert das ehrlich (Abweichung von Spec „holdout", Begründung im Validierungs-Report).

## 8. Test-Strategie (Kurzfassung, Detail je Phasen-PR)

Spec-Sektion 33 gemappt: 33.1 Detection → Capability-Service-Tests + Real-Korpus (dev-Split-Stichprobe je Template-Familie); 33.2/33.3 Extraktion+Mapping → Mapper-Unit-Tests + `*.real-files.test.ts` gegen `~/work/qaf-corpus` (Muster `detect-g60-corpus-sweep`); 33.4 Workflow → Action-Tests (Transaktion, Idempotenz, Permissions, Rollback); 33.5 Editing → Editor-Feld-Status-Tests (Logik-Ebene); 33.6 Sync → P5; 33.7 Analyse → metrics-Tests; 33.8 Regression → bestehende Suiten (3900+ Tests) + gezielte VSM-Bestands-Tests (manuell erzeugte Streams unverändert lesbar/editierbar). Jeder Test „rot ohne Fix" wo anwendbar (Programm-Doktrin).

## 9. Sicherheits-Leitplanken

- Neue Tabelle mit `_own`+`_admin`-RLS ab Migration Tag 1; RLS-Tests im Harness (`scripts/rls-test/run.sh` + portable PG17-Variante).
- Kosten-Daten verlassen nie den Projekt-Scope (E4); Lineage-Panel zeigt Quelle nur Nutzern mit QAF-Zugriff (gleiche RLS-Kette).
- Kein IDOR über `qafFileId`-Parameter: Capability-/Preview-Actions selektieren über RLS-gefilterte Queries, 0 Zeilen = „nicht gefunden" (kein Existenz-Leak).
- Input-Validierung an der Action-Boundary (zod, Repo-Muster `env-schema`-Stil).
