# QAF → Wertstrom: Current-State-Analyse

Stand: 2026-07-17 · Programm QVS (QAF-to-Value-Stream-Integration) · Analyse-Basis: Voll-Erkundung des Repos (3 parallele Read-only-Sweeps) + Korpus-Inventar `~/work/qaf-corpus`.

Zweck: Dokumentierter Ist-Zustand BEVOR die Integration gebaut wird — was existiert, was ist wiederverwendbar, wo enden Fertigungsdaten heute. Die Lücken-Bewertung steht separat in `qaf-value-stream-gap-analysis.md`.

## 1. Zwei QAF-Pipelines (Befund: parallel, unterschiedliche Reife)

**A) Haupt-Engine `qaf-differences`** (KAR-799ff., produktiv):
- Upload über signierte Storage-URLs (`qaf-uploads`-Bucket), Server Actions in `app/qaf-differences/actions.ts` (~4712 Zeilen, 22 exportierte Server Actions + 17 interne Hilfsfunktionen). Kern: `ingestQafUpload` (Download → SHA-256 → Safety-Checks → `loadExcelWorkbook` → Foreign-Form-Detection → Multi-QAF-Detection → Verzweigung G60 / Multi-QAF / Standard).
- Standard-Pfad ruft `parseQAFTemplate` (`lib/qaf-parser.ts`) und persistiert Fertigungszeilen **relational** in `qaf_manufacturing_step` (`row_index`, `position_number`, `process_name`, `machine_name`, `part_name`, `raw_values JSONB`, `normalized JSONB`, `source_cells JSONB`).
- Sonstige Modul-Ergebnisse landen im JSONB-Bag `qaf_file.g60_meta` (templateFingerprint, material/sbm/rmr/logistics/lccn/co2e, capability_matrix, multiQafContainer, …).
- `qaf_file.file_hash` (SHA-256) wird geschrieben und indiziert, aber **nirgends gelesen** — Dedup-/Cache-Logik ist vorbereitet, nicht verdrahtet.

**B) Legacy-Pfad „QAF Process Comparison Board"** (KAR-341/704): client-seitiges Parsing in `components/qaf/qaf-client.tsx`, Roh-Zeilen als `qaf_uploads.parsed_data` JSONB; verbindet QAF-Zeilen mit `process_steps` über `qaf_process_mappings` (`lib/qaf/process-mapping.ts`, `scoreCandidate`). Läuft unter `app/project/[id]/qaf` und `app/lsc-workshop/[id]/qaf`.

## 2. Fertigungsdaten-Extraktion heute (Standard-QAF-Pfad)

Kanonisches Modell: `QAFRowValues` (`lib/qaf-parser.ts:96-119`) = 22 MANUFACTURING-Canonical-Fields (`lib/qaf-differences/internal/canonical-fields.ts:992-1298`, IDs `mfg_*`; Gesamt-Registry 246 Felder über 12 Module).

Je Prozesszeile heute extrahiert:

| Feld | Canonical-ID | Bemerkung |
|---|---|---|
| Prozessname | `mfg_process_designation` | |
| Sequenz | `mfg_position_number` + `row_index` | Quell-Reihenfolge, keine explizite Verkettung |
| Zykluszeit | `mfg_cycle_time` | EIN undifferenzierter Wert, Einheit s |
| Teile pro Zyklus | `mfg_parts_per_cycle` | |
| Operator-Anzahl | `mfg_direct_employee_count` | |
| Maschinenstundensatz | `mfg_machine_hour_rate` | |
| Lohnkosten/-satz | `mfg_direct_labor_cost` + `mfg_social_overhead_rate` | |
| Prozesskosten | `mfg_direct_manufacturing_cost`, `mfg_remaining_overhead_rate`, `mfg_manufacturing_cost_bw/_aw` | |
| Rüstkosten | `mfg_setup_cost_per_unit` | KOSTEN, keine Zeit |
| Ausschuss | `mfg_scrap_rate` + `mfg_scrap_cost` | |
| Standort | `mfg_site` | Freitext |
| Anlage/Maschine | `mfg_facility_designation` | Freitext |
| Währung | `mfg_procurement_currency`, `mfg_quotation_currency`, `mfg_exchange_rate` | |

Sheet-Erkennung: `qaf-parser.ts` matcht Substrings `fertigungskosten|manufacturing cost|manufactering cost` (erstes Match gewinnt, Fallback erstes Sheet). Header→Feld-Mapping seit KAR-893 über das kanonische Registry (Tier 1 exakt 1.0 / Tier 2 Alias 0.9 / sonst unmapped).

**Nicht extrahiert** (Details in Gap-Analyse): Rüst-ZEIT, Wartezeit, Transportzeit je Schritt, Zykluszeit-Split manuell/maschinell/automatisch, Kapazität (`sum_planned_capacity`: „not parsed by Kadi-v2 today"), Losgröße (`sum_lot_size`: dito), Schichtmodell pro Schritt (nur `sum_shifts_per_week` je Datei). Unmapped Spalten werden nur als Header-Text in `unmappedHeaders` protokolliert — Zellwerte werden nie gelesen.

## 3. Capability-Schicht (KAR-957/959 — das geforderte semantische Fundament)

- `capability/sheet-resolver.ts`: `resolveSheetRole(sheetName)` → `summary|material|manufacturing|sbm|rmr|logistics|lccn|co2e|setup|input|premise|unknown`. Additiv zu `module-sheet-names.ts` inkl. Korpus-Aliassen: **„lv detail" → manufacturing, „production cost" → manufacturing, „labor/labour value detail" → manufacturing**, „rüstkosten/setup cost" → setup, „prämissenblatt/lohnsatz" → premise. Confidence-Cap 0.9.
- `capability/capability-detector.ts` + `field-registry.ts` + `module-capability-resolver.ts`: `WorkbookCapabilityMatrix { sheetRoles, modules: ModuleCapability[status: AVAILABLE|PARTIAL|DERIVABLE|MISSING|PARSE_FAILED], assumptions, modelVersion }` → persistiert in `g60_meta.capability_matrix`.
- Status heute: **rein diagnostisch** — das `ModuleCapability`-Statusraster hat keinen Konsumenten; einzig das Teilfeld `sheetRoles` fließt bereits in die Not-a-QAF-Warnung (`actions.ts:1479-1490` → `detectNotAQaf` → Plausibilitäts-Hinweis in der UI, kein Gate). Die QVS-Integration wird der erste echte Konsument des Rasters.
- `capability/field-candidates.ts`: Multi-Source-Konfliktauflösung (`FieldResolution: NONE|SINGLE|AGREEMENT|INCONSISTENT`, „preserve all candidates, never silently discard").

## 4. Source-Lineage & Confidence (vorhanden, mehrschichtig)

- Zell-Provenienz je Feld: `QAFRow.sourceCells` (`"Fertigungskosten!W15"`) + `rawText` + `normalized` → 1:1 in `qaf_manufacturing_step.source_cells`.
- Formel-Provenienz: `formula-engine.ts` (`FormulaProvenance`, Shared-Formula-Auflösung, Diff-Status `formel_geaendert`/`formel_zu_konstante`).
- Parse-Confidence: `QAFParseMeta.parseConfidence` + Objekt-Confidences im Multi-QAF-Modell (bewusst „coarse, not calibrated").
- Explain-Infrastruktur: `explain-provenance.ts` (Tri-State `present|not_applicable|not_captured`, „nie fabrizieren") + UI `qaf-explain-panel.tsx`, `qaf-provenance.tsx`.

## 5. Multi-QAF und Varianten

- 21 Module, ~17.900 Zeilen (`lib/qaf-differences/internal/multi-qaf/`, KAR-925/929-951): Container-Assembly, VariantDefinition (stabile Identität via `compositeCanonicalKey`), VirtualQafVariant, 4-Stufen-Variant-Matcher, Differ-Familie, Rekonziliation, Aggregate-Impact, Compare-Flow, Export.
- **Strukturelle Grenze:** Eine Variante trägt Fertigungsdaten NUR als geteilte Profil-**Aggregate** (`SharedCostProfile`, i. d. R. `{totalPerUnit}`) — es gibt **keine variantenspezifische Prozessschritt-Liste** (`bridge.ts:86-100` dokumentiert das explizit; `steps` bleibt in `toCanonicalInputs` bewusst absent).
- `multiQafDetection.enabled=true` seit KAR-925 (13.07.2026); EngineConfig versioniert (`1.4.0`), Rollback-Runbook `docs/runbooks/multi-qaf-rollout.md`.

## 6. /Wertstrom-Modul (VSM) heute

**Datenmodell** (`value_stream_maps`, Bootstrap `supabase-bootstrap-from-prod.sql:1618-1630`): 1 Zeile = 1 Diagramm; `nodes JSONB` + `connections JSONB` + `layout JSONB` (faktisch tot), `project_id` NULLABLE, `is_demo`. **Keine Versionierung, kein Node-Audit, keine Node-PKs auf DB-Ebene.**

`VsmNode` (`lib/vsm-types.ts:4-26`): 7 Node-Typen (`process|machine|inventory|transport|customer|supplier|timevalue`), Pflicht `id,type,x,y,name`; Timing **bereits vorhanden**: `cycleTimeSec, machineTimeSec, manualTimeSec, setupTimeSec, waitTimeSec, transportTimeSec`; außerdem `isValueAdded?: boolean` (binär), `capacityPerHour, oee, quantity, distance, demand, numWorkers, processType, machineType, notes`. **Kein einziges Kosten-Feld, kein Scrap, keine Varianten-Zugehörigkeit, keine Herkunft, kein Import-Status.**

**API** (`app/api/wertstrom/`): CRUD; Autorisierung = „eingeloggt" + RLS `vsm_own` (`created_by = auth.uid() OR project_id IN eigene Projekte`) + Demo-Read. Permission-Codes `vsm.read/write` existieren in DB, werden im Code nie geprüft. PUT ist Partial-Update auf Dokument-Ebene (kompletter Array-Replace für nodes/connections).

**Editor** (`vsm-editor.tsx`, 1042 Z., tdd-guard:skip): Canvas mit Pan/Zoom/Grid-Snap, Add/Delete/Connect/Move, Inline-Edit je Typ, Bottleneck-Badge + Takt-Warnung gegen `projects.customer_takt_time_sec`. Kein Undo, kein Auto-Layout, Titel im Editor nicht editierbar, Save/Autosave (30 s) sendet nur `{nodes, connections}`. Desktop/Maus-only, UI hartcodiert Deutsch (i18n nur Nav-Label + Admin-Tooltips).

**Metriken** (`vsm-metrics.ts`, 31 Z.): `findBottleneckId` (max cycleTimeSec), `computeTimeline` (VA = Σ cycleTime der `isValueAdded`-Nodes; NVA = Σ cycle+wait der übrigen; flache Summe **ohne Graph-Traversal** — Parallelpfade würden falsch addiert). Kein Kosten-/Scrap-Rollup, keine echte Durchlaufzeit.

**Bestehende Importe** (alle OHNE Herkunfts-Markierung):
1. Excel-Import (`vsm-excel.ts` + Modal): fixes deutsches Spalten-Template, jede Zeile → process-Node, `isValueAdded` hartcodiert `true`.
2. LSC-Import (`vsm-editor.tsx:217-282`): `process_steps` + Outlier-bereinigtes Mittel aus `cycle_measurements` (reimplementiert `avgCycleTimeSec` inline statt `lib/reporting/aggregations` zu nutzen), Fallback `planned_cycle_time_sec`; umgeht die API (Browser-Supabase direkt).
3. Stoppuhr-Picker (`vsm-stoppuhr-picker-modal.tsx`): setzt `cycleTimeSec = planned_cycle_time_sec` (SOLL-Wert, kein Messwert).
Zusätzlich Inline-Mini-Stoppuhr im Panel (lokaler Timer, persistiert nichts). `lib/stopwatch/` ist die getrennte, robustere Mess-Engine (localStorage-Resilienz, `evaluation.ts` mit `avgCycleTimeSec` als Single-Source-of-Truth).

**Kein Export** (PDF/Excel/Report), keine Offline-Registrierung (`value_stream_maps` fehlt in `lib/offline/registry.ts`).

**Tests**: 18 Unit-Tests für reine Helfer (geometry/metrics/excel-mapping/config) + Demo-Seed-Formtests. Null Abdeckung für API-Routen, Editor-Interaktion, beide Import-Flows; keine e2e.

## 7. UI-Orte mit Fertigungsprozess-Daten (Entry-Point-Kandidaten)

1. `app/project/[id]/qaf` (`qaf-client.tsx`): Upload, Versions-Tabs, Vergleichs-Board, Mapping-Dialog „Mapping ↔ Prozess-Steps" — einziger Ort mit existierender QAF↔process_steps-Datenstruktur.
2. `app/qaf-differences/[id]` (comparison_mode `summary`/`g60`): zeigt `qaf_manufacturing_step` via `qaf-comparison-detail.tsx` (Step-Matching, Produktionssicht mit 6 der 22 Felder, Explain-Panel, Provenance) — neben bestehenden Export-Buttons.
3. `app/qaf-differences/[id]/kalkulator`: hat `project_id` + aufgelöste Step-Liste.
4. `/wertstrom`-Liste (`vsm-list-client.tsx`): Create-Formular (heute `title/description/project_id`) — die Empfänger-Seite.
5. `app/project/[id]`-Übersicht: verlinkt heute NICHT zu /wertstrom (Sichtbarkeits-Lücke der neuen Verbindung).

## 8. Permissions & Datensicherheit

- Projekt-Zugriff wird primär von **RLS** durchgesetzt: 14 `qaf_*`-Tabellen mit `_own` (project-owner) + `_admin`-Policy-Paar; App-Code prüft meist nur Login bzw. `isAtLeastRole('admin')`. Kein kosten-spezifischer Permission-Code; `project_consultants.role_in_project='cost_engineering'` ist nur deskriptiv.
- `qaf_process_mappings` hatte historisch eine laxe Policy (`auth.uid() IS NOT NULL`), seit KAR-890 (`supabase-migration-qaf-process-mappings-rls-fix.sql`, operator-applied 09.07.2026) trägt sie das projekt-scoped `_own`+`_admin`-Paar — heute kein Ausreißer mehr.
- RLS-Tests: `__tests__/security/rls-qaf-differences-isolation.test.ts` („Confidential BMW/supplier cost data must never leak across owners"), Runner `scripts/rls-test/run.sh` (Docker) bzw. portable PG17-Variante.
- **Veraltet, nicht verwenden:** `docs/authorization_model.md` + `docs/feature_entitlement_model.md` (Entitlement-Layer am 26.05.2026 entfernt; `requireEntitlement`/`requirePermission` existieren nicht im Code).

## 9. Feature-Flags

`config/profiles/` (APP_PROFILE `default|bmw|_template`); die äußere Profil-Hülle `CompositionProfileSchema` ist `.strict()`, die `FeatureFlagsSchema`-Felder sind required (nicht optional) — neues Flag = Schema-Feld + Wert in allen 3 Profilen + `npm run check:profiles`. Flag `wertstrom` existiert (bmw: true). Kein QAF-Flag (QAF fest an); Multi-QAF läuft über versionierte EngineConfig, nicht über Profile.

## 10. Korpus & Test-Infrastruktur

- `~/work/qaf-corpus`: 374 kanonische Workbooks (`incoming/QAFs/<name>`; Manifest 376 Records, ok=373, 3 legacy_biff), `profiles/` (376 Struktur-JSONs + corpus.duckdb), Capability-Matrix-Stichprobe 80/374 (Kernbefund: RMR ~0 %, SBM schwächste Facette).
- Splits: `dev=227`, `validation=70`, `holdout=75` (deterministisch, stratifiziert). **Holdout wurde am 16.07.2026 (KAR-963/P6) bereits einmalig verbraucht** — nicht mehr jungfräulich. Kein challenge-Split. `clusters/ golden/ schemas/ patterns/ normalized/ mutations/ logs/` sind leere Scaffolds.
- Batch-Runner: `cd ~/work/kadi-v2 && npx tsx ~/work/qaf-corpus/tools/batch-runner.ts <input-dir> <output-json>` (cwd-Pflicht wegen `@/`-Alias); fährt denselben Code-Pfad wie der Ingest (`loadExcelWorkbook→detectG60→detectMultiQaf→buildTemplateFingerprint`). Letzter Voll-Lauf 15.07.2026: 95,7 % standard_summary, 2,7 % confirmed_multi_qaf.
- Repo-seitig: 25 `*.real-files.test.ts` (6 gegen qaf-corpus — u. a. `detect-g60-corpus-sweep` als Dev-Split-Regression; 19 gegen das separate 19-Workbook-Set `qaf-compare-kar824/input`).

## 11. Wo Fertigungsdaten heute enden

1. DB relational: `qaf_manufacturing_step` (Standard-Pfad, vollständig mit Lineage).
2. DB Diff: `qaf_step_match` + `qaf_manufacturing_diff`.
3. UI: `qaf-comparison-detail.tsx` (Produktionssicht zeigt 6/22 Felder), Explain/Provenance, Kosten-Charts.
4. XLSX-Export (`internal/export.ts`).
5. Legacy: `qaf_uploads.parsed_data` → Comparison-Board → `qaf_process_mappings` → `process_steps`.
6. Multi-QAF: Schritt-Ebene existiert nicht — nur Profil-Aggregate in `qaf-multi-qaf-detail.tsx`.
7. **Nirgends: /Wertstrom.** Die Kette endet vor dem VSM-Modul; `components/wertstrom/` enthält keinerlei QAF-Bezug.
