# QAF → Wertstrom: Korpus-Validierung & Qualitätsmetriken (QVS-P6)

Stand: 2026-07-18 · Programm QVS · P6 (KAR-975) · Basis: `qaf-value-stream-corpus-evidence.md` (P1), `qaf-value-stream-field-mapping.md`, `mappings/qaf-to-value-stream-mapping.yaml` (MAPPING_VERSION `qvs-1`). Vorbedingung: Feature-Flag `qafValueStream` bleibt in allen 3 Profilen `false` — P6 ist die repräsentative Validierung VOR einem möglichen Flag-Flip (P7 braucht Kais' explizites Go), keine Prod-Wirkung.

## 1. Methodik

**Splits (kanonisch, `~/work/qaf-corpus/splits/`, deterministisch SHA-256-basiert, 16.07.2026):**

| Split | Dateien | Rolle in P6 |
|---|---|---|
| `dev.json` | 227 | Kalibrierung — Methodik/Tooling gegen die volle Bandbreite geprüft, hier NICHT die berichteten Zahlen |
| `validation.json` | 70 | **Berichtete Metriken** — die Zahlen unten sind die für das Programm verbindlichen |
| `holdout.json` | 75 | TABU — durch KAR-963 (16.07.) bereits verbraucht (Klassifikations-Holdout eines anderen Workstreams). Kein erneuter Zugriff, kein Ersatz-Holdout fabriziert. Siehe `unresolved-limitations.md`. |

Keine Datei kommt in mehr als einem Split vor (verifiziert: 0 Überlappungen dev↔validation↔holdout).

**Tooling:** neues, reines Read-Only-Measurement-Skript `~/work/qaf-corpus/tools/p6-vsm-corpus-validate.ts` (gleiche Konvention wie `batch-runner.ts`/P1 und `qvs-evidence-scan.mjs`/P1 — lebt außerhalb von kadi-v2, keine Supabase-Persistenz, keine Produkt-Code-Änderung). Fährt jede Split-Datei durch **dieselbe** Pipeline wie die echte App:

`lib/qaf-parser.parseQAFTemplate` → `lib/qaf-value-stream.assessManufacturingCapability` + `mapQafRowsToVsmNodes` → `buildPreviewFromRows` (die reine, DB-freie Hälfte von `buildQafValueStreamPreview` — siehe `preview.ts`-Moduldoc: „kept separate ... so it can be exercised directly against real-corpus-parsed QAFRow[] without a database, same discipline as mapper.real-files.test.ts").

Reproduzierbar:
```bash
cd /home/aria/work/kadi-v2
npx tsx /home/aria/work/qaf-corpus/tools/p6-vsm-corpus-validate.ts \
  /home/aria/work/qaf-corpus/splits/dev.json \
  /home/aria/work/qaf-corpus/reports/p6-dev-results.json dev
npx tsx /home/aria/work/qaf-corpus/tools/p6-vsm-corpus-validate.ts \
  /home/aria/work/qaf-corpus/splits/validation.json \
  /home/aria/work/qaf-corpus/reports/p6-validation-results.json validation
```
Gelaufen 2026-07-18. Dev: 227/227 Dateien verarbeitet (0 fehlten auf Platte), 104,6 s. Validation: 70/70 Dateien, 36,8 s.

**Review-Fix (PR #340, Finding 1 — Beleg-Lücke geschlossen):** Das Tool speicherte pro Datei bisher nur `missingFieldReasonCount` (eine reine Summe über alle 12 Felder) — die in §4 zitierte Reason-Code-Aufschlüsselung je Feld war damit aus den beiden Output-JSONs nicht ableitbar. Additiv erweitert um `missingFieldReasonsByField: Record<Feld, Record<ReasonCode, Anzahl>>` (bestehende Keys unverändert, `not_numeric[<rawText>]`-Varianten zu einem `not_numeric`-Bucket zusammengefasst). BEIDE Splits mit dem erweiterten Tool erneut komplett gelaufen, 2026-07-18: Dev 103,4 s, Validation 38,5 s. Diff gegen den Vor-Fix-Output (alle Felder außer der reinen Laufzeit und dem neuen additiven Feld): **0 Abweichungen** über alle 227+70 Dateien — jede in diesem Report zitierte Kernzahl reproduziert exakt, kein Determinismus-Problem. Rohdaten (inkl. Dateinamen) liegen ausschließlich lokal unter `~/work/qaf-corpus/reports/` — **nicht committet** (KAR-943). Dieser Report enthält ausschließlich aggregierte Kennzahlen; wo ein Einzelfall benannt wird, nur über seinen numerischen Split-Index (`dev#N`/`validation#N`, dieselbe Konvention wie `mapper.real-files.test.ts`s Moduldoc: „only its numeric dev.json index is used, never its name").

**Was diese Messung NICHT abdeckt (bewusste Scope-Grenzen, keine Lücken-Überraschung):**
- **Prozess-Count "erwartet" (Ground-Truth):** Es existiert **kein** Ground-Truth-Datensatz für erwartete Prozessschritt-Zahlen im Korpus-Repo — geprüft: `manifests/` (nur Format-/Ladestatus, keine Prozesszahlen), `profiles/*.json` (heuristische Kandidaten-Regionen mit expliziter Konfidenz, selbst als „nie als Fakt" deklariert, keine bestätigte Ground Truth), kein `golden/`-Verzeichnis mit erwarteten Prozesszahlen, kein Treffer für `groundTruth`/`expectedProcessCount` irgendwo in kadi-v2 oder qaf-corpus. Diese Messung berichtet daher nur **"erkannt"** (deskriptiv); der "erkannt vs. erwartet"-Vergleich aus dem KAR-975-Scope bleibt strukturell unbeantwortbar, bis ein manuell erstelltes Ground-Truth-Set existiert. Eskaliert in `unresolved-limitations.md`.
- **Echte DB-Creation** (`createValueStreamFromQaf`, SECURITY DEFINER RPC): braucht eine echte Supabase-Verbindung — läuft hier nie (P6 ist lokal/pur). Abgedeckt durch die bestehenden gemockten Unit-Tests (`creation.test.ts`) + die P2-RLS-Regressionstests, beide Teil der Voll-Suite (siehe `qaf-value-stream-regression-results.md`). Was P6 stattdessen misst — der volle Preview-Assembly-Pfad (Capability + Mapper + Fingerprint + Titel-Vorschlag) —, ist der rechenintensive Kern, den `createValueStreamFromQaf` vor dem eigentlichen Schreiben identisch neu ableitet (`qaf-source.ts` `mapAndFingerprint`, „single choke point"-Kommentar).
- **Multi-QAF-Varianten-Erstellungspfad** (`createValueStreamsForVariants`): verifiziert (Code-Lesung, kein separater Zeilen-Quellpfad — `mapQafRowsToVsmNodes` wird ausschließlich aus `qaf-source.ts` aufgerufen, jede Variante ist einfach ein eigener `qaf_file`, normal geparst). Der Full-Sweep unten deckt diesen Pfad deshalb strukturell mit ab; keine gesonderte Variantenschleife nötig.

## 2. Kernzahlen — dev-Split (Kalibrierung, n=227)

| Metrik | n | min | median | mean | max |
|---|---|---|---|---|---|
| loadOk | 227 | — | — | 100,0 % (227/227) | — |
| parseOk | 227 | — | — | 91,2 % (207/227) | — |
| capabilityEligible (von parsed) | 207 | — | — | 96,1 % (199/207) | — |
| previewOk (von parsed) | 207 | — | — | **100,0 % (207/207)** | — |
| stepCount ("erkannt", nur eligible) | 199 | 1 | 3 | 6,99 | 53 |
| nodeCount | 207 | 0 | 3 | 6,72 | 53 |
| connectionCount | 207 | 0 | 2 | 5,76 | 52 |
| fieldsPresentFraction (Lineage) | 199 | 90,9 % | 100,0 % | 98,4 % | 100,0 % |
| durationMs/Datei | 227 | 72 | 223 | 461 | 9 468 |

Sequenz-Genauigkeit (unter 199 Dateien mit ≥1 Node): **196 `row_order`, 3 `ambiguous`** (98,5 %). Die 3 Ambiguous-Fälle: `dev#36`, `dev#165` (beide `Standard: QAF_LEGACY_DE_SUMMARY`), `dev#198` (`Standard: QAF_V9_SUMMARY`) — `positionsnummer` widerspricht in jeweils genau einem Fall der Zeilenreihenfolge; Mapper-Verhalten ist by-design konservativ (Zeilenreihenfolge bleibt primär, siehe `mapper.ts` `assessSequence`), kein Fehler.

**Cross-Check gegen die bestehende P1-Baseline** (`mapper.real-files.test.ts`, CHANGELOG 2026-07-18 „QVS-P1"-Eintrag: *„227 Dateien, 207 geparst / 20 Parse-Fehler / 0 auf Platte fehlend, 202 Dateien mit Schritten, 1472 Fertigungsschritte, 91,4 % Zykluszeit-Abdeckung, 0 Mapper-Fehler"*): P6 reproduziert **exakt** 207 geparst/20 Fehler und Σrows=1472 (summe aller geparsten Zeilen inkl. solcher ohne `prozessbezeichnung`). Die Differenz 202 (P1 „Dateien mit Zeilen") vs. 199 (P6 „capabilityEligible") sind genau 3 Dateien mit Zeilen, aber ohne eine einzige mit Prozessname (`dev#`-Familien: 2× `MultiQAF: mqaf_v1`, 1× `Standard: QAF_LEGACY_DE_SUMMARY`) — verifiziert, keine Diskrepanz. Die Zykluszeit-Zahl 91,4 % (P1) vs. 96,8 % (P6, siehe Feld-Coverage-Tabelle unten) unterscheidet sich NUR im Nenner: P1 teilt durch alle 1472 geparsten Zeilen (inkl. der 81 ohne Prozessname), P6 teilt durch die 1391 tatsächlich eligible Schritte (`CapabilityAssessment.fieldCoverage`s eigene, dokumentierte Definition: „Fraction of eligible steps"). Zähler ist in beiden Fällen identisch (1346): 1346/1472 = 91,4 % (P1) = 1346/1391 = 96,8 % (P6). Beide Zahlen sind korrekt, unterschiedliche (dokumentierte) Nenner-Wahl — kein Widerspruch.

## 3. Kernzahlen — validation-Split (berichtet, n=70)

| Metrik | n | min | median | mean | max |
|---|---|---|---|---|---|
| loadOk | 70 | — | — | 100,0 % (70/70) | — |
| parseOk | 70 | — | — | 91,4 % (64/70) | — |
| capabilityEligible (von parsed) | 64 | — | — | 96,9 % (62/64) | — |
| previewOk (von parsed) | 64 | — | — | **100,0 % (64/64)** | — |
| stepCount ("erkannt", nur eligible) | 62 | 1 | 4 | 6,69 | 47 |
| nodeCount | 64 | 0 | 3,5 | 6,48 | 47 |
| connectionCount | 64 | 0 | 2,5 | 5,52 | 46 |
| fieldsPresentFraction (Lineage) | 62 | 90,9 % | 100,0 % | 98,0 % | 100,0 % |
| durationMs/Datei | 70 | 113 | 227 | 526 | 3 717 |

Sequenz-Genauigkeit (unter 62 Dateien mit ≥1 Node): **62 `row_order`, 0 `ambiguous` (100,0 %)**.

Die validation-Zahlen liegen in jeder Kennzahl innerhalb ±1,5 Prozentpunkten der dev-Kalibrierung (parseOk 91,2 % vs. 91,4 %; eligible 96,1 % vs. 96,9 %; previewOk je 100 %) — der stratifizierte Split hat keine systematische Verzerrung zwischen den beiden Stichproben erzeugt, ein gutes Zeichen für die Repräsentativität beider Splits.

## 4. Feld-Coverage je gemapptem Feld (12 `PRIMARY_FIELD_MAPPINGS`, Basis: eligible Dateien)

„step-weighted" = Σ(Feld gesetzt)/Σ(alle eligible Schritte) über den ganzen Split (die belastbarere Zahl — gewichtet nach tatsächlicher Schritt-Masse). „file-level" = Verteilung des PRO-DATEI-Anteils (kann bei kleinen Dateien nur grob gestuft sein, z. B. 2-Schritt-Dateien nur 0/50/100 %).

| Feld (QAF-Quelle → VSM-Ziel) | dev step-weighted | dev file-level (median/mean) | validation step-weighted | validation file-level (median/mean) |
|---|---|---|---|---|
| zykluszeit → cycleTimeSec | 96,8 % | 100,0 % / 96,6 % | 95,2 % | 100,0 % / 96,3 % |
| teileProZyklus → partsPerCycle | 93,7 % | 100,0 % / 95,6 % | 92,3 % | 100,0 % / 95,6 % |
| anzahlMA → numWorkers | 89,0 % | 100,0 % / 85,2 % | 86,3 % | 100,0 % / 86,0 % |
| bezeichnungAnlage → machineType | 84,8 % | 100,0 % / 84,3 % | 78,1 % | 100,0 % / 74,6 % |
| standort → location | 98,6 % | 100,0 % / 97,5 % | 94,2 % | 100,0 % / 94,2 % |
| beschaffungswaehrung → currency | 95,8 % | 100,0 % / 92,8 % | 96,6 % | 100,0 % / 94,2 % |
| ausschuss → scrapRate | 94,7 % | 100,0 % / 95,3 % | 88,4 % | 100,0 % / 91,2 % |
| ausschusskosten → scrapCostPerUnit | 83,0 % | 100,0 % / 91,6 % | 77,6 % | 100,0 % / 88,0 % |
| mss → machineHourRate | 92,7 % | 100,0 % / 94,2 % | 88,0 % | 100,0 % / 92,1 % |
| lohnkosten → laborHourRate | 81,1 % | 100,0 % / 79,0 % | 79,8 % | 100,0 % / 78,5 % |
| **ruestkosten → setupCostPerUnit** | **19,7 %** | 50,0 % / 45,5 % | **21,4 %** | 50,0 % / 47,5 % |
| fk → costPerUnit | 92,9 % | 100,0 % / 95,3 % | 91,6 % | 100,0 % / 95,3 % |

**Ausreißer, nicht weggemittelt:** `ruestkosten` (Rüstkosten → `setupCostPerUnit`) liegt in BEIDEN Splits konsistent weit unter allen anderen 11 Feldern (19,7 %/21,4 % step-weighted gegenüber 78–99 % bei allen übrigen). Das ist eine reale, reproduzierbare Korpus-Eigenschaft, keine Tooling-Schwäche: Rüstkosten werden in den Quell-QAFs erkennbar nur für einen Teil der Fertigungsschritte je Datei ausgewiesen (nicht jeder Prozessschritt trägt eine eigene Rüstkosten-Position), UND größere (schritt-reiche) Dateien haben tendenziell eine noch dünnere Rüstkosten-Abdeckung als kleine (deshalb liegt der step-weighted-Wert klar unter dem file-level-Mittel von ~46 %) — die 5 Worst-Performer in §9 bestätigen das Muster (bei 5 von 5 ist `ruestkosten=0%` der schärfste Einzelbefund; Review-Fix PR #340 Finding 4: vorher fälschlich „4 von 5" und falsche Sektionsangabe „§6" statt „§9"). **Nicht zu verwechseln** mit der bereits in `qaf-value-stream-corpus-evidence.md` dokumentierten Rüst-**ZEIT** (0/227, ein anderes, zeitbasiertes Feld, das dieser Mapper nie befüllt — siehe §7).

**Root-Cause-Check, jetzt tool-nativ reproduzierbar (Review-Fix, PR #340 Finding 1):** Bisher stand hier eine Reason-Code-Aufschlüsselung ohne nachvollziehbare Quelle im dokumentierten Tool/Output — das ist mit der additiven `missingFieldReasonsByField`-Erweiterung (s. §1) behoben; die folgenden Zahlen sind jetzt direkt aus den beiden, per §1-Kommandos reproduzierbaren Output-JSONs summierbar, keine undokumentierte Zusatzanalyse mehr. Aufsummiert über `missingFieldReasonsByField.ruestkosten` im vollen dev-Split (227 Dateien): **1117/1117 = 100 % `cell_empty`, 0 % `no_column_mapped`** — die Spalte wird in jedem einzelnen Fall korrekt lokalisiert, die Zelle ist im Quelldokument selbst leer. Das schließt einen Parser-/Header-Erkennungs-Bug für dieses Feld aktiv aus — die niedrige Abdeckung ist ausschließlich eine Quelldaten-Eigenschaft. Zum Vergleich, `lohnkosten` (zweitniedrigstes Feld, aber mit 81,1 % noch innerhalb der normalen Bandbreite), aufsummiert über `missingFieldReasonsByField.lohnkosten`: **183 `no_column_mapped` / 30 `cell_empty` / 50 `not_numeric`** (Summe 263) — hier lokalisiert der Parser die Spalte in einem spürbaren Teil der Dateien gar nicht erst. Beide Zahlensätze reproduzieren exakt die vorher (ohne die Tool-Erweiterung, damals nicht auditierbar) berichteten Werte — keine Abweichung, nur die Beleglücke ist jetzt geschlossen. Nicht weiter vertieft (kein Ausreißer-Feld, keine der beiden Splits zeigt `lohnkosten` unter den Top-Ursachen der Worst-Performer in §9) — als kleine, unentschiedene Beobachtung in `unresolved-limitations.md` vermerkt, NICHT als Bug behauptet (dafür wäre eine manuelle Sichtung der Quell-Header nötig, außerhalb des P6-Scopes).

## 5. Lineage (qafSource-Vollständigkeit)

`nodesWithQafSource`: **100,0 %** in beiden Splits (1391/1391 dev, 415/415 validation) — jeder gemappte Node trägt `qafSource` (strukturell garantiert durch `mapper.ts`, siehe `mapAndFingerprint`, kein Fall ohne). `fieldsPresentFraction` (Anteil der 22 `ALL_FIELD_KEYS` mit einem `qafSource.fields`-Eintrag, je Node) liegt bei median 100 %, mean 98,4 % (dev) / 98,0 % (validation), min 90,9 % — d. h. selbst der schwächste Fall hat noch 20 von 22 Feldern mit Herkunftsangabe (Zelle/Wert/Konfidenz/`mappedTo`). Die Lineage-Kette (Feld gesetzt ⇒ `qafSource.fields`-Eintrag vorhanden) ist zusätzlich strukturell durch `mapper.real-files.test.ts` als Hart-Invariante über den ganzen dev-Split erzwungen (kein `mappedTo`-Feld ohne Lineage-Eintrag).

## 6. VA-Klassifikation (konservativ — nie automatisch `va`)

| Klasse | dev (n=1391 Nodes) | validation (n=415 Nodes) |
|---|---|---|
| `va` | **0 (0,0 %)** | **0 (0,0 %)** |
| `nva` (Transport/Lager/Nacharbeit) | 13 (0,9 %) | 6 (1,4 %) |
| `nnva` (Prüfen/Messen/Kontrolle) | 104 (7,5 %) | 34 (8,2 %) |
| `unknown` | 1274 (91,6 %) | 375 (90,4 %) |

Bestätigt exakt das dokumentierte Design (`va-classification.ts`): **kein einziger Node** wird je automatisch als wertschöpfend klassifiziert, auch nicht bei eindeutig klingenden Namen wie „Schweißen"/„Montieren" — ein Mensch entscheidet `va`, nie der Import. Die 91 % `unknown` sind daher keine Erkennungslücke, sondern die gewollte, konservative Grundhaltung.

## 7. Creation-/Preview-Ergebnis

**100,0 % der geparsten Dateien** (207/207 dev, 64/64 validation) durchlaufen `buildPreviewFromRows` **fehlerfrei** — 0 Mapper-Fehler, 0 Preview-Fehler in beiden Splits. Node-/Kantenzahlen siehe §2/§3-Tabellen (Kanten = Nodes−1 je Datei, linear verkettet, by design). Diese 100 %-Zahl ist kein Zufall, sondern die bereits bestehende Hart-Garantie aus `mapper.real-files.test.ts` (KAR-970, „mapQafRowsToVsmNodes darf über den vollen dev-Split nie werfen") — P6 bestätigt sie erneut UND erweitert sie erstmals auf den validation-Split (der bisher keine eigene volle Sweep-Messung hatte) sowie auf die volle Preview-Orchestrierung (nicht nur den nackten Mapper-Aufruf).

## 8. Schnitt je Template-Familie

Familien-Klassifikation aus dem Fingerprint-Modul: Grobklassifikation (`primaryClassification`/`matchedFamily`) direkt aus der bestehenden, deterministischen Split-Stratifizierung (`splits/*.json`, selbst aus `buildTemplateFingerprint`/`detectMultiQaf`/G60-Erkennung abgeleitet, KAR-957); Multi-QAF-Feinklassifikation (`mqaf_v1`/`mqaf_v2`/`81_custom`) live neu berechnet über `fingerprintMultiQafTemplateFromExcelJs` (`lib/qaf-differences`), da die Split-Stratifizierung selbst nur grob nach `matchedProfile` schneidet, nicht nach `MultiQafTemplateFamily`.

**dev (n=227):**

| Familie | n | parseOk | eligible (von parsed) | mean stepCount | mean fieldsPresentFraction |
|---|---|---|---|---|---|
| Standard: QAF_LEGACY_DE_SUMMARY | 164 | 163/164 (99 %) | 162/163 | 7,0 | 98,5 % |
| Standard: QAF_V9_SUMMARY | 48 | 38/48 (79 %) | 33/38 | 5,2 | 98,3 % |
| Standard: kein Profil (none) | 6 | 0/6 (0 %) | — | n/a | n/a |
| MultiQAF: 81_custom | 3 | 3/3 (100 %) | 3/3 | 20,7 | 93,9 % |
| MultiQAF: mqaf_v1 | 2 | 2/2 (100 %) | 0/2 | n/a (0 eligible) | n/a |
| G60_DETAIL | 2 | 0/2 (0 %) | — | n/a | n/a |
| Ambiguous (unknown_multi_qaf) | 1 | 0/1 (0 %) | — | n/a | n/a |
| MultiQAF: mqaf_v2 | 1 | 1/1 (100 %) | 1/1 | 17,0 | 100,0 % |

**validation (n=70):**

| Familie | n | parseOk | eligible (von parsed) | mean stepCount | mean fieldsPresentFraction |
|---|---|---|---|---|---|
| Standard: QAF_LEGACY_DE_SUMMARY | 50 | 50/50 (100 %) | 50/50 | 6,8 | 98,4 % |
| Standard: QAF_V9_SUMMARY | 15 | 13/15 (87 %) | 11/13 | 4,5 | 97,1 % |
| MultiQAF: 81_custom | 3 | 1/3 (33 %) | 1/1 | 26,0 | 90,9 % |
| Standard: kein Profil (none) | 2 | 0/2 (0 %) | — | n/a | n/a |

**Einordnung, ehrlich (kein Wegmitteln):**
- **„Standard: kein Profil (none)" parst 0/6 (dev) und 0/2 (validation) — durchgängig 100 % Fehlschlag.** Erwartbar und strukturell, kein Regressionsbefund: „none" heißt per Definition, dass keine der bekannten Summary-Profile (`QAF_LEGACY_DE_SUMMARY`/`QAF_V9_SUMMARY`) gematcht hat — `parseQAFTemplate` braucht ein erkennbares Fertigungskosten-Sheet-Layout, das diese Dateien laut eigener Stratifizierung nicht haben.
- **`G60_DETAIL` (0/2, nur dev) und `Ambiguous` (0/1, nur dev) ebenfalls 0 % parseOk** — strukturell erwartet: G60 ist ein komplett anderes Template (eigene G60-Detailerkennung, kein Fertigungskosten-Sheet), `parseQAFTemplate` wurde nie für dieses Format gebaut. Deckungsgleich mit der etablierten Doktrin aus `mapper.real-files.test.ts`s Moduldoc: „Parse-level failures are... known, pre-existing parseQAFTemplate behavior, not a QVS-P1 regression."
- **`MultiQAF: mqaf_v1` (dev, n=2): 2/2 geparst, aber 0/2 eligible** — beide Dateien liefern Zeilen (3 bzw. 12), aber keine trägt einen Prozessnamen. Kleine Stichprobe (n=2), keine belastbare Familien-Aussage möglich, nur als Einzelbefund vermerkt.
- **`MultiQAF: 81_custom` in validation (1/3, 33 %) weicht von dev (3/3, 100 %) deutlich ab** — bei **n=3** ist das eine Kleinstichproben-Schwankung, keine gesicherte Familien-Eigenschaft. Nicht schöngerechnet: die validation-Zahl wird so berichtet, wie gemessen, mit dem ausdrücklichen Hinweis auf die geringe Stichprobengröße (Konsequenz der ursprünglichen Stratifizierung: nur 9 Multi-QAF-Dateien insgesamt über beide Splits — dev 6 [`81_custom` n=3 + `mqaf_v1` n=2 + `mqaf_v2` n=1] + validation 3 [`81_custom`]; Review-Fix, PR #340 Finding 5 — vorher fälschlich als „8" gezählt, ein Zählfehler gegen die eigenen Familientabellen oben).
- **`ruestkosten` fällt bei `81_custom` besonders stark ins Gewicht** (mean fieldsPresentFraction 93,9 %/90,9 % — niedrigster Wert aller Standard-Familien) — konsistent mit dem in §4 benannten Muster, hier familien-konzentriert sichtbar.

## 9. Worst-Performer (Top 5, validation-Split — die berichtete Stichprobe)

Ranking-Kriterium: **„missing-value mass" = (1 − mittlere Feld-Coverage) × stepCount** — bevorzugt Dateien, die tatsächlich viele fehlende Feldwerte zum Korpus beitragen, statt kleine 2-3-Schritt-Dateien, die bei groben Verhältniszahlen zufällig auf einem niedrigen Prozentwert landen (Referenzliste ohne Gewichtung am Ende dieses Abschnitts, zur Einordnung).

| Rang | Datei (Referenz) | Familie | missing-value mass | mittlere Feld-Coverage | Schritte | Hauptursache (niedrigste Felder) |
|---|---|---|---|---|---|---|
| 1 | `validation#52` | Standard: QAF_LEGACY_DE_SUMMARY | 7,9 | 83,2 % | 47 | `ruestkosten`=0 %, `ausschusskosten`=74 %, `anzahlMA`=83 % |
| 2 | `validation#1` | MultiQAF: 81_custom | 7,3 | 71,8 % | 26 | `lohnkosten`=0 %, `ruestkosten`=0 %, `zykluszeit`/`teileProZyklus`=85 % |
| 3 | `validation#45` | Standard: QAF_LEGACY_DE_SUMMARY | 4,2 | 83,0 % | 25 | `ruestkosten`=0 %, `ausschusskosten`=72 %, `anzahlMA`=84 % |
| 4 | `validation#46` | Standard: QAF_LEGACY_DE_SUMMARY | 4,2 | 83,0 % | 25 | `ruestkosten`=0 %, `ausschusskosten`=72 %, `anzahlMA`=84 % |
| 5 | `validation#49` | Standard: QAF_LEGACY_DE_SUMMARY | 3,8 | 83,0 % | 22 | `ruestkosten`=0 %, `ausschusskosten`=64 %, `anzahlMA`=86 % |

**Ursache, konsolidiert (Review-Fix, PR #340 Finding 4 — vorheriger Text nannte fälschlich „4/5", obwohl die Tabelle direkt darüber bereits alle 5 Zeilen mit `ruestkosten`=0 % auswies):** In 5/5 Fällen ist `ruestkosten` (Rüstkosten) das schwächste Feld — und in allen 5 Fällen exakt 0 %, nicht nur in 4 von 5. In `validation#1` liegt zusätzlich `lohnkosten` gleichauf bei 0 % (einziger Fall der Top 5 mit zwei gleich schwachen Feldern statt einem). Derselbe Korpus-weite Effekt aus §4, hier konzentriert bei den größeren (22-47-Schritt) Dateien — kein datei-individueller Ausreißer, sondern die Spitze des bereits benannten Korpus-Musters. Der Dev-Split zeigt dasselbe Muster an seiner eigenen Spitze (`dev#1`/`dev#2`, beide `MultiQAF: 81_custom`, je `ruestkosten=0%`/`lohnkosten=0%` als Hauptursache) — Familien-übergreifend reproduzierbar, nicht Zufall einer Stichprobe.

Referenz, ungewichtet nach reiner Feld-Coverage % (kann bei kleinem stepCount an Zufalls-Ties liegen — z. B. `validation#15` mit nur 4 Schritten landet bei 8,3 % mittlerer Coverage, weil dort zufällig fast alle 12 Felder leer sind, nicht weil das ein „typischer" Fall ist): `validation#15` (8,3 %, 4 Schritte), `validation#16` (54,2 %, 8 Schritte), `validation#53` (58,3 %, 3 Schritte), `validation#54` (63,9 %, 3 Schritte), `validation#27` (66,7 %, 11 Schritte).

## 10. Bekannte, bereits dokumentierte Grenzen (referenziert, nicht neu entdeckt)

Diese Punkte sind aus früheren Phasen bereits belegt und werden hier **nicht erneut hergeleitet**, nur für den P6-Gesamtkontext zitiert:

- **Zeit-Split-Felder (Rüstzeit, Handzeit, Maschinenzeit, Automatikzeit, Wartezeit, Transportzeit je Schritt): 0/227** Quell-Präsenz (P1-Scan, `qaf-value-stream-corpus-evidence.md`) — VSM-Zeitfelder bleiben beim QAF-Import leer, keine Fabrikation. Nicht zu verwechseln mit `ruestkosten` (§4 dieses Reports, ein KOSTEN-Feld, das real vorkommt, nur unvollständig).
- **`plannedCapacity` 124/227 (54,6 %)** und **`lotSize` 195/227 (85,9 %)** — Datei-Ebene-Felder, gemessen in P4 (`qaf-value-stream-corpus-evidence.md` Nachtrag, nach labelScan-Plausibilitäts-Guard). P6 misst diese nicht erneut (kein P6-Scope-Feld, `fileLevelContext` bleibt in dieser No-DB-Messung strukturell `null`, siehe §1).
- **Zykluszeit-Coverage 91,4 % (P1-Rohmessung, andere Nenner-Wahl)** — siehe §2 Cross-Check-Absatz, hier vollständig aufgelöst (kein Widerspruch zu den 96,8 % dieses Reports).
- **Rename-Grenze (P5):** eine im Quelldokument umbenannte Station hat keine stabile Identität und erscheint bei einem Reimport als `REMOVED_FROM_SOURCE`+`NEW_IN_SOURCE` statt eines einzelnen `SOURCE_CHANGED` — bewusst keine Positions-Fallback-Heuristik gebaut (CHANGELOG „QVS-P5"-Eintrag). Berührt P6 nicht direkt (P6 misst Erst-Import, nicht Reimport), aber relevant für die Gesamtbewertung der Pipeline-Reife.
- **KAR-977 / KAR-941 (Altlasten):** im Programm-Kontext als bekannte, vorbestehende Posten benannt — **keine Dokumentation dieser beiden Ticket-IDs in kadi-v2 (reports/, CHANGELOG.md, TODO.md, docs/) oder in der Git-Historie gefunden.** Da Linear laut Auftrag nicht angefasst werden soll, kann P6 ihren Inhalt nicht verifizieren oder einordnen — zitiert hier nur als Platzhalter-Referenz, inhaltlich offen. Siehe `unresolved-limitations.md`.
- **vaClass konservativ:** siehe §6 dieses Reports — vollständig im P6-Scope gemessen, kein reiner Verweis.

## 11. Vertraulichkeit

Dieser Report enthält ausschließlich aggregierte Kennzahlen und Split-Indizes (`dev#N`/`validation#N`) — keine Dateinamen, keine Lieferantennamen, keine Zellwerte, keine Preise (KAR-943). Die Rohdaten (`~/work/qaf-corpus/reports/p6-{dev,validation}-results.json`, inkl. Dateinamen und SHA-256) liegen ausschließlich lokal außerhalb dieses Repos und werden nicht committet.
