# Loop-10-Rest: „gemeinsame Table/Filter/Evidence-Engines · Virtualisierung großer Tabellen · URL-State für Filter"

Read-only Bestands-/Gap-Analyse. Repo `/home/aria/work/kadi-v2`, Branch `main`,
Commit `4e0658b`. Analyse-Stand 2026-08-15. Kein Code geändert.

Auftrags-Scope: der in `loop-reports/loop-10-teil3-2026-08-12.md:68-69`
("Bekannte Grenzen") benannte Rest-Block, identisch zitiert in
`program-status.md:478-479` als letzter Offene-Punkte-Eintrag von Loop 10.
PO-gated Themen (G60/Multi-QAF, deferred-Exporte, KI-Live B-7,
recompare-Spaltung) wurden nicht angefasst und fließen nur als
Abgrenzungs-Beleg ein.

---

## 1. Was meint die Doku konkret?

**Kurzfassung: Stichwort-Liste, keine Spec.** Die drei Punkte stehen exakt so —
als dreiwortige Bullet-Kette ohne eigenen Absatz — an genau zwei Stellen im
Repo, beide mit demselben Wortlaut:

- `docs/qaf-v2/loop-reports/loop-10-teil1-2-2026-08-11.md:72-73` — erste
  Nennung, als „was offen bleibt" nach Teil 1+2.
- `docs/qaf-v2/loop-reports/loop-10-teil3-2026-08-12.md:68-69` — wiederholt
  nach Teil 3, dort mit Zusatz: „(Bereichs-Deep-Links existieren seit Loop 8)".
- `docs/qaf-v2/program-status.md:478-479` — Übernahme aus teil3 in die
  zentrale Übersicht.

Keine dieser drei Stellen definiert, was „Filter Engine" oder „URL-State"
konkret bedeuten (welche Felder, welche Query-Parameter, welche Seiten). Es
gibt **keinen eigenen Analyse-/Spec-Abschnitt** für diesen Rest-Block — anders
als z. B. für die View-Spec-Adoption, die eine eigene 300-Zeilen-Gap-Analyse
bekam (`view-spec-adoption-analyse.md`).

**Ursprung der Stichworte — Roadmap-Aufgabenliste, nicht Exit-Kriterium.** Der
Wortlaut kommt aus der Aufgabenliste (nicht den Exit-Kriterien) von Loop 10 in
der Baseline, wortgleich an zwei Stellen im Repo (Baseline wird laut
Programmstatus-Konvention nicht umgeschrieben):

- `docs/qaf-v2/baseline-inputs/06_Autonomous_Loop_Roadmap_v1.0.md:396-402`
- `docs/qaf-v2/baseline-inputs/01_QAF_Comparator_Master_Specification_v1.0.md:4928-4934`
  (Anhang-Abschnitt „14. Loop 10", inhaltlich identische Kopie der Roadmap)

Wortlaut dort (9 Zeilen, unser Scope ist eine Teilmenge — „Evidence Panel"/
„Table Engine"/„Filter Engine"/„Virtualisierung"/„URL-State"; „Empty State
Komponenten" und „Comparison A-F Header" gehören zur selben Liste, sind aber
**nicht** Teil des hier untersuchten Rest-Blocks, siehe Auftrag):

```
- gemeinsames Evidence Panel.
- gemeinsame Table Engine.
- gemeinsame Filter Engine.
- gemeinsame Empty State Komponenten.
- gemeinsame Comparison A-F Header.
- Virtualisierung für große Tabellen.
- URL-State für Filter und Auswahl.
```

Die zugehörigen **Exit-Kriterien** von Loop 10
(`06_Autonomous_Loop_Roadmap_v1.0.md:404-409`) sind qualitativ, kein Schwellwert:
„keine zentrale Detailkomponente über vereinbartem Größenlimit" · „alle
Ansichten lesen View Specs" · „keine fachliche Berechnung im Client" ·
„**Tabellen bleiben bei großen QAFs interaktiv**". Nur der letzte Punkt
berührt unseren Scope, bleibt aber ohne Zahl.

**Eine Zahl gibt es — an einer vierten, unabhängigen Stelle:** die
Requirements Traceability Matrix, nicht der Loop-Text selbst:

`docs/qaf-v2/baseline-inputs/07_Requirements_Traceability_Matrix_v1.0.md:85`
```
| PER-001 | Performance | Große Tabellen virtualisieren | Teilweise | Schwellen vereinheitlichen | 200+ rows virtualization | P1 | UI | 10 | Scroll und filter performant |
```
PER-001 ist Loop 10 zugeordnet (Spalte „Loop" = 10), Status „Teilweise" (RTM-
Stand, nicht code-verifiziert), Ziel „200+ rows virtualization", Abnahme
„Scroll und filter performant". Das ist die einzige konkrete Schwelle im
gesamten Repo für diesen Rest-Block — sie steht in der RTM, nicht im
Loop-10-Text, und „Schwellen vereinheitlichen" impliziert selbst, dass heute
uneinheitliche/keine Schwellen existieren (bestätigt unter Frage 5: keine
Schwelle im Code gefunden).

**Zwei Quell-Abschnitte der Master Spec motivieren das Thema, gehören aber
NICHT zum Loop-10-Scope:**

- **Kap. 32 „Tabellen für bis zu sechs QAFs"**
  (`01_QAF_Comparator_Master_Specification_v1.0.md:2190-2217`) ist die
  einzige Stelle mit einer ausformulierten Tabellen-/Filter-/Virtualisierungs-
  Spec („erste Spalte bleibt fixiert", „Tabellenkopf bleibt fixiert", „große
  Tabellen werden virtualisiert", „Filter und Suche bleiben sichtbar", eigene
  Mobile-Darstellung). Diese Spec ist aber explizit die **Multi-QAF-Tabelle
  (1–6 Dateien)** — das ist **Loop 7** („Comparison Set für ein bis sechs
  QAFs"), Status laut Programmstatus „nicht begonnen"
  (`program-status.md:347-364`) und laut Blocker-Register **B-1 vom PO
  deferred** („Multi QAF erstmal hinten anstellen", 14.08.2026,
  `docs/qaf-v2/blocker-register.md:10`). Ein Teil der Motivation hinter dem
  Loop-10-Stichwort „Virtualisierung" ist damit an einem Feature verankert,
  das gerade nicht gebaut werden soll.
- **Kap. 34.3 „Performance bei großen QAFs"**
  (`01_QAF_Comparator_Master_Specification_v1.0.md:2290-2307`) beschreibt vor
  allem **Server-/Parsing-Performance** bei > 464.000 belegten Zellen im
  CALCULATION-Blatt der Zeilen-QAF (Worker, Cache, gestufte Bereitstellung) —
  „Tabellenvirtualisierung" ist dort ein Bullet unter vielen, nicht
  ausgearbeitet.

**Real und im Scope (Einzel-QAF, nicht Multi-QAF) ist dagegen Kap. 6.4
„Extrem große Tabellen"** — eine IST-Zustandsbeobachtung des Vorgänger-Systems,
mit konkreten Zeilenzahlen (siehe Frage 2). Das ist die einzige Stelle mit
belegbaren Größenordnungen für die tatsächlich betroffenen Tabellen der 5
Detail-Module.

**Fazit Frage 1:** Der Block ist eine **unausgearbeitete Stichwort-Liste**.
Es gibt kein Soll-Bild (keine Ziel-UI, keine Query-Parameter-Namen, keine
Komponentenschnitt-Vorgabe). Die einzige harte Zahl (PER-001, 200+ Zeilen)
kommt aus einem Nachbardokument und ist selbst als „Teilweise" mit Aufgabe
„Schwellen vereinheitlichen" geführt. Ein Teil der Spec-Motivation (Kap. 32)
gehört zu einem PO-deferred Feature. **Scope-Klärung mit Kais ist nötig,
bevor mehr als der unten skizzierte Konsolidierungs-Schritt gebaut wird** —
Details unter Frage 6.

---

## 2. Tabellen-Bestand

### 2.1 Die 5 Detail-Module (`components/qaf-differences/detail-sections/`)

| Datei:Zeile | Sektion | Zeilenquelle | Eigene Sortier-/Filter-Logik? |
|---|---|---|---|
| `fertigungskosten-sections.tsx:50` | `ProductionSection` (Produktionssicht) | `production.rows` (`ProductionRateRow[]`, aus `buildManufacturingRun`/`production-view.ts`) | Nein — reines `.map()`, keine Sortierung/Filterung im Modul |
| `fertigungskosten-sections.tsx:151` | `AnomaliesSection` (Master-Sätze) | `anomalies.rows` (`MasterRateRow[]`) | Nein |
| `fertigungskosten-sections.tsx:282` | `ManufacturingDeltasSection` (A1-Deltas) | `deltas.rows` (`ManufacturingDeltaRowSpec[]`, aus `buildManufacturingRun`) | Nein |
| `zusammenfassung-sections.tsx:59` | `FormSection` (Formular-Positionen) | `form.lines` (`FormLine[]`, aus `buildSummaryRun`) | Nein |
| `zusammenfassung-sections.tsx:202` | `MetricsSection` (Kennzahlen) | `metrics.rows` (`MetricsTableRow[]`, feste `SUMMARY_ROW_REGISTRY`, 20 Einträge — `lib/qaf-differences/internal/summary-row-registry.ts:70-90`) | Nein |
| `massnahmen-sbm-sections.tsx:175` | `LeversSection` (Hebel, Listen-Layout, kein `<table>`) | `levers` (`LeverSpec[]`, aus `buildLeversRun`) | Nein |
| `massnahmen-sbm-sections.tsx:256` | `OneTimeSection` (Einmalzahlungen) | `oneTimeRows` (`OneTimeRow[]`) | Nein |
| `anhang-sections.tsx:31-45` | `StructureChangesSection` (A2, `<ul>`/Karten statt `<table>`) | `structure.neu/entfallen/umstrukturiert` (vorgruppiert von `buildAppendixRun`, seit Modul 3 14.08.) | Nein (Gruppierung liegt seit Modul 3 im Builder, **nicht** mehr im Renderer — Stand aktueller Code; ältere `view-spec-adoption-analyse.md:123` beschreibt noch die Vorher-Version mit Inline-`.filter()`, das ist überholt) |
| `anhang-sections.tsx:51-88` | `StepMatchingSection` (A3, delegiert an `QafStepMatching`) | delegiert | Ja, siehe unten (Sub-Komponente) |
| `anhang-sections.tsx:136-170` | `PlausibilitySection` (A4, `<ul>`) | `plausibility.rows` (vorklassifiziert von `buildAppendixRun`) | Nein |
| `ueberblick-sections.tsx` | `OverviewSection`/`SummaryKpiTiles`/`FazitSection` | keine Tabelle (Kacheln/Grid) | — |

Alle sechs `<table>`-Vorkommen in den Modulen (`fertigungskosten-sections.tsx`
×3, `zusammenfassung-sections.tsx` ×2, `massnahmen-sbm-sections.tsx` ×1) sind
**unabhängige, handgeschriebene** `<table className="w-full text-sm">`-Blöcke
mit eigenem `<thead>`/`<tr>`/`.map()` — keiner importiert eine gemeinsame
Tabellen-Komponente. Kein Modul hat Sortierung, Filterung, Suche oder eine
„N Zeilen ausgeblendet"-Transparenz — jede Tabelle zeigt **immer alle**
übergebenen Zeilen.

### 2.2 Sub-Komponente mit eigener Schreib-/Zuordnungslogik

`components/qaf-differences/qaf-step-matching.tsx:95` — eigenes `<table>`,
eigener `useState`/`useMemo`-Zustand (`assigned`, `touched`,
`qaf-step-matching.tsx:51-70`) für die manuelle Prozessschritt-Zuordnung. Das
ist eine **Schreibpfad**-Tabelle (Zuordnung ändern), kein Lese-/Filter-Fall —
laut `view-spec-adoption-analyse.md:130` bewusst als eigenes Risiko geführt.

### 2.3 V2-Seite (`app/qaf-differences/[id]/v2/page.tsx`)

| Datei:Zeile | Tabelle | Zeilenquelle | Engine? |
|---|---|---|---|
| `v2/page.tsx:448` | Abdeckungs-Tabelle (Capability-Coverage je Bereich) | `coverageAward`/`coverageCurrent` (≤ 24 Module) | Nein, eigenes `<table>` |
| `v2/page.tsx:522,538,549,565,584,595` | 6× `<QafV2DifferencesTable spec={…}>` (MFG/MAT × Wert-Struktur/Formel-ohne-Wert/ohne-Wert-und-Formel) | `TableSpec` aus **`buildTable()`** (`lib/qaf-differences/internal/table-specs.ts:109-148`) | **Ja** — einzige echte Engine im Repo (siehe unten) |

`components/qaf-differences/qaf-v2-differences-table.tsx:64-106` ist der
Renderer für `TableSpec` — zeigt Kennung, Station·Feld, Zuordnung,
Vergabestand, Aktueller Stand, Δ, Beleg (`BelegSpalte`/`BelegZelle`,
Zeile 24-58) sowie die Transparenz-Zeile „X von Y Zeilen · N Zeile(n)
ausgeblendet" (Zeile 97-103).

### 2.4 Die vorhandene Engine — `lib/qaf-differences/internal/table-specs.ts`

Das ist der wichtigste Einzelbefund dieser Analyse: **eine Table/Filter-Engine
existiert bereits, pure, getestet, mit genau der im Loop-10-Stichwort
verlangten Eigenschaft „gemeinsame Tabelle, gemeinsame Filter, gemeinsame
Sortierung"** (Datei-Kopf `table-specs.ts:1-18`):

- `TableRow` (Zeile 22-40), `TableFilter` (Zeile 42-53: `matchTypes`,
  `buckets`, `changedOnly`, `zeroCostOnly`, `withFindingsOnly`, `search`),
  `TableSort` (Zeile 55: `'sheet_order' | 'delta_desc' | 'delta_asc'`),
  `TableSpec` (Zeile 57-69).
- `buildTable()` (Zeile 109-148) — filtert, sortiert, zählt Ausgeblendetes
  samt Delta-Summe, erzeugt die Transparenz-Notiz.
- `rowsFromRecords()` (Zeile 157-178) — `DifferenceRecord[]` → `TableRow[]`.
- `validateTable()` (Zeile 192-211) — Gegenprobe: angezeigt + ausgeblendet
  muss die Gesamtsumme ergeben (verhindert stillen Summenverlust beim
  Filtern).
- 18 Tests in `lib/qaf-differences/internal/__tests__/table-specs.test.ts`
  (162 Zeilen).

**Aber:** Diese Engine ist **nur an die V2-Seite verdrahtet**, und selbst dort
nur mit **fest im Server-Code vorgegebenen Partitionen**
(`v2/page.tsx:222-224,245`: `mfgTable`/`mfgFormelTable`/
`mfgUnveraendertTable` = drei feste Partitionen derselben Datenmenge), **nicht**
mit einem interaktiven Filter, den der Nutzer bedienen kann (siehe Frage 3).
Die 6 `<table>`-Blöcke in den Klassik-Modulen (2.1) nutzen sie **gar nicht** —
sie sind eine komplett separate, parallele Implementierung.

### 2.5 Duplikation heute — Belegt

| Muster | Fundstellen | Bewertung |
|---|---|---|
| Hand-`<table className="w-full text-sm">` mit eigenem `<thead>`/`.map()` | `fertigungskosten-sections.tsx:50,151,282`, `zusammenfassung-sections.tsx:59,202`, `massnahmen-sbm-sections.tsx:256`, `v2/page.tsx:448`, `qaf-step-matching.tsx:95` (8 Stellen) | Reale, zählbare Duplikation — gleiches CSS-Muster, gleiche Zeilen-Iteration, kein gemeinsamer Baustein |
| Test-/getriebene Filter-/Sortier-Engine | `table-specs.ts` (nur an V2-Seite verdrahtet) | Existiert bereits — Konsolidierungsziel, kein Neubau |
| „N ausgeblendet"-Transparenz (U-08-Pflicht) | Nur `table-specs.ts`/`QafV2DifferencesTable` | Fehlt in allen 8 Klassik-/Coverage-Tabellen — heute unkritisch, weil dort ohnehin nicht gefiltert wird, aber ein struktureller Nachholbedarf, sobald Filter dort ankommen |

### 2.6 Größenordnung realer Zeilenzahlen — Beleg aus der Spec (IST-Beobachtung des Vorgänger-Systems)

`01_QAF_Comparator_Master_Specification_v1.0.md:855-864` ("Extrem große
Tabellen", Abschnitt 6.4 "Aktuelle Hauptprobleme"):

```
- 104 Stationen in der Produktionssicht
- 375 Fertigungskosten-Deltas
- 167 Strukturänderungen
- 192 Prozesszuordnungen
- 1.002 Plausibilitätsbefunde
```

Zuordnung zu den heutigen Modulen:

- 104 → `ProductionSection` (`fertigungskosten-sections.tsx:50`)
- 375 → `ManufacturingDeltasSection`/A1 (`fertigungskosten-sections.tsx:282`)
- 167 → `StructureChangesSection`/A2 (`anhang-sections.tsx`)
- 192 → `StepMatchingSection`/A3 (`anhang-sections.tsx` → `qaf-step-matching.tsx:95`)
- 1.002 → `PlausibilitySection`/A4 (`anhang-sections.tsx`)

**Wichtiger Vorbehalt:** Diese Zahlen sind eine **Ist-Beobachtung des
Vorgänger-Systems** zum Zeitpunkt der Spec-Erstellung, **keine V2-Messung**.
Es gibt im Repo **keinen** Performance-Test, Loop-Report-Befund oder
Kommentar, der eine tatsächliche Render-Verzögerung im heutigen V2-Code
(React/Next.js) dokumentiert — weder in `loop-10-teil1-2-2026-08-11.md` noch
in `loop-10-teil3-2026-08-12.md` noch in `blocker-register.md` (dort taucht
Performance nur unter B-8, visuelle Regression, und PER-002, Multi-QAF-Budget,
auf — nicht als V2-Renderproblem der Klassik-Tabellen). Die 5 genannten Werte
(104-1.002) sind trotzdem plausible, dokumentierte Belege dafür, dass reale
Mappen Tabellen weit über der RTM-Schwelle „200+ rows" erzeugen können,
insbesondere A4 (1.002) und A1 (375).

Für `zusammenfassung-sections.tsx` (Form/Metrics) und
`massnahmen-sbm-sections.tsx` (Levers/OneTime) liegt **keine** dokumentierte
Zeilenzahl vor — Kennzahlen-Tabelle ist strukturell klein (20 feste Zeilen,
`summary-row-registry.ts:70-90`), für Formular-Positionen und Hebel/
Einmalzahlungen wurde keine Größenangabe gefunden (hier nicht behauptet).

---

## 3. Filter-Bestand

**Kernbefund: es gibt in den 5 Klassik-Detail-Modulen und ihrer Schale keine
einzige interaktive Filter-UI.** Keine Suchbox, keine Checkbox, kein
Dropdown — geprüft per Grep in `qaf-comparison-detail.tsx` (0 Treffer für
„filter"/„Filter" außer einem unabhängigen Array-`.filter()` in einem
Testfile) und in allen sechs Detail-Section-Dateien (0 `useState` für
Filterzweck).

### 3.1 Wo Filter-*Zustand* heute tatsächlich existiert

| Ort | Datei:Zeile | State | Persistenz |
|---|---|---|---|
| Vergleichs-**Listen**seite (nicht Detail-Ansicht) | `components/qaf-differences/qaf-differences-client.tsx:101-103` (`listQuery`, `statusFilter`, `tagFilter`) | `useState`, rein lokal | **Keine** — Reload/Teilen verliert den Filter; kein URL-Sync (Grep bestätigt: keine `useSearchParams`/`router.push` in dieser Datei für diese States) |
| V2-Detailseite, Hauptreiter-Auswahl (Loop 8) | `components/qaf-differences/qaf-primary-navigation.ts:31` (`PRIMARY_AREA_PARAM = "bereich"`) | **URL** (`?bereich=<id>`) | Ja — das ist das einzige existierende URL-State-Beispiel im QAF-Bereich, aber für Navigation, nicht für Zeilen-Filter |
| V2-Tabellen (`table-specs.ts`) | `TableFilter`-Typ existiert (`table-specs.ts:42-53`) | **Serverseitig fest verdrahtet** (`v2/page.tsx:222-224,245`: feste Partitionen), **kein** clientseitiger Zustand, kein `useState`, keine UI-Kontrolle | N/A — es gibt (noch) nichts zu persistieren |
| Klassik-Detailtabellen (5 Module) | — | Kein Filter-Zustand vorhanden | N/A |

### 3.2 Was „URL-State für Filter" konkret bedeuten würde

Weil Punkt 3.1 zeigt, dass es **noch keine interaktive Filter-UI** gibt (weder
in der Klassik-Schale noch nutzerbedienbar auf der V2-Seite), ist „URL-State
für Filter" heute **kein Zustands-Migrationsproblem** (kein bestehender
`useState` müsste nach `useSearchParams` verschoben werden), sondern setzt
eine noch nicht getroffene Entscheidung voraus: **welche** der bereits im
`TableFilter`-Typ modellierten sechs Dimensionen (`matchTypes`, `buckets`,
`changedOnly`, `zeroCostOnly`, `withFindingsOnly`, `search`) überhaupt als
sichtbare UI-Kontrolle erscheinen sollen, und auf welchen Seiten (nur V2? auch
Klassik? beide parallel?).

**Kollisionsprüfung mit `?bereich=`:** Kein Namenskonflikt gefunden — die
einzige belegte bestehende Query-Param-Konvention ist `bereich` für den
Hauptreiter (Loop 8, `qaf-primary-navigation.ts:31`). Ein Filter-Param
bräuchte einen eigenen, klar unterscheidbaren Namensraum (z. B. Präfix
`f_`/`filter_`), sonst wären künftig zwei semantisch verschiedene
Konzepte (aktiver Reiter vs. Zeilenfilter) im selben flachen `?`-Namensraum
schwer auseinanderzuhalten, sobald beide auf derselben Seite koexistieren
(V2-Seite hat aktuell keinen `?bereich=`-Verbrauch, die Klassik-Seite hat ihn
seit Loop 8, `app/qaf-differences/[id]/page.tsx:172`).

**Fazit Frage 3:** „URL-State für Filter" ist aktuell ein Vorgriff auf eine
Filter-UI, die es noch nicht gibt. Die technische Grundlage (Filter-Typ,
Query-Logik) existiert bereits pure/getestet in `table-specs.ts`; was fehlt,
ist (a) eine UI-Bindung, (b) eine Entscheidung, welche Filter sichtbar werden,
und (c) danach die URL-Synchronisierung selbst — technisch trivial mit
Next.js' `useSearchParams`/`router.replace` (kein neues Package nötig, siehe
Frage 5).

---

## 4. Evidence-Panel-Bestand

**Kernbefund: es gibt heute DREI unterschiedliche, nicht zusammenhängende
„Beleg zeigen"-Mechanismen** — nicht einen gemeinsamen, wie der Loop-10-
Stichwort „gemeinsames Evidence Panel" nahelegt.

### 4.1 `QafV2EvidencePanel` (`components/qaf-differences/qaf-v2-evidence-panel.tsx`)

- **Props:** `{ rows: EvidenceRow[], labelDe?, defaultOpen? }`
  (Zeile 65-70). `EvidenceRow` kommt aus `view-specs.ts` (`evidenceFor()`,
  Zeile 17-Import).
- **Zeigt:** Datei-Rolle, Blatt!Zelle (kopierbar, Zeile 111-121), Formel,
  Wertzustand, Befund-IDs — als aufklappbare Tabelle (Zeile 96-131).
- **Konsument: NUR** `components/qaf-differences/qaf-v2-reference-section.tsx:54`
  — und die wird **nur** von `app/qaf-differences/referenz/page.tsx:62`
  gerendert, der **Referenzansicht mit erfundenen Beispieldaten**
  (bestätigt auch in `program-status.md:164-166`: „zeigt IDs, aber
  ausdrücklich mit erfundenen Beispieldaten — sie ist die Vorlage für die
  Verdrahtung, nicht der Gegenbeweis"). **Kein produktiver Vergleich mit
  echten Daten rendert diese Komponente.**

### 4.2 `BelegSpalte`/`BelegZelle` (inline in `qaf-v2-differences-table.tsx:24-58`)

- Kein eigenständiges Panel — eine `<span>`-Spalte **innerhalb** jeder Zeile
  der 6 produktiven `QafV2DifferencesTable`-Instanzen auf der echten V2-Seite.
  Nutzt dieselben Label-Helfer wie 4.1 (`cellReference`, `fileLabel`,
  `isWeakState`, `valueStateLabel`, importiert aus
  `qaf-v2-evidence-panel.tsx:15` — **nur die Helferfunktionen**, nicht die
  Panel-Komponente selbst).
- Das ist der **tatsächlich produktiv sichtbare** Beleg-Mechanismus für
  Fertigungs-/Material-Differenzen auf der V2-Seite — aber nicht aufklappbar/
  konsistent mit 4.1, sondern immer inline sichtbar.

### 4.3 `QafExplainPanel` (`components/qaf-differences/qaf-explain-panel.tsx`)

- **Props:** `{ attrs: ExplainAttributes, altLabel?, neuLabel? }`
  (Zeile 206-212). `ExplainAttributes` kommt aus
  `lib/qaf-differences/internal/explain-provenance.ts` (§17
  Master-Prompt-Traceability-Set: `canonicalFieldId`, `sourceFile/-Sheet/-Cell`,
  `rawValue`, `normalizedValue`, `formula`, `mappingMethod`, `confidence`,
  `unitConversion`, `currencyConversion`, `templateProfileVersion`,
  `calculatedDelta`, `comparisonRule`, `validationResult`, `engineVersion`,
  `timestamp` — 15 Attribute, `qaf-explain-panel.tsx:172-201`). Das ist ein
  **strikter Superset** von `EvidenceRow` (4.1) — deutlich reichhaltiger
  (Mapping-Methode, Confidence, Einheiten-/Währungsumrechnung, Engine-Version).
- **Konsumenten (produktiv, echte Daten):**
  `fertigungskosten-sections.tsx:320` (A1-Deltas, Modul 4) und
  `zusammenfassung-sections.tsx:220` (Kennzahlen, Modul 5) — jeweils als
  Toggle-Button je Tabellenzeile, kein zentrales Panel.

### 4.4 Bewertung

Diese drei Mechanismen haben **unterschiedliche Datenformen**
(`EvidenceRow` vs. rohes `DifferenceCell[]` vs. `ExplainAttributes`),
**unterschiedliche UI-Muster** (aufklappbares Panel vs. Inline-Spalte vs.
Pro-Zelle-Toggle) und **keinen gemeinsamen Konsumenten** — eine
Vereinheitlichung ist kein reines Umbenennen, sondern eine echte
Datenmodell-Entscheidung (wird `EvidenceRow` zu einer Teilmenge von
`ExplainAttributes`, oder bleibt die Trennung Zellnachweis (SRC-002) vs.
volle §17-Provenienz bestehen?). Das ist **eigenständiger Aufwand**, nicht
Teil des unten empfohlenen risikoarmen ersten Schritts (Frage 6).

### 4.5 Abgrenzung zum Loop-5-Rest „Evidence-Panel-Ausbaustufe"

`program-status.md:260-263,278-279` führt unter Loop 5 einen separaten
Punkt: „Evidence-Panel-Ausbaustufe (SRC-002-Kette sichtbar, s. o.)" — und
verweist selbst auf den direkt darüberstehenden Absatz „SRC-002-Kette
sichtbar seit 12.08.: die U-08-Tabellen zeigen je Zeile
Rolle→Blatt!Zelle→Formel→Wertzustand aus dem Katalog". Das ist exakt
Mechanismus 4.2 (`BelegSpalte` in `QafV2DifferencesTable`) — der Loop-5-
Punkt bezieht sich auf **das Sichtbarmachen der Zell-Beleg-Kette in den
U-08-Tabellen selbst** und ist laut Programmstatus-Text bereits umgesetzt
(12.08.). Der hier untersuchte Loop-10-Punkt „gemeinsames Evidence Panel"
ist etwas anderes: die **Konsolidierung der drei unter 4.1-4.3 beschriebenen
Mechanismen zu einem wiederverwendeten Baustein über alle Sektionen**. Beide
Punkte teilen das Wort „Evidence Panel", sind aber unterschiedliche Aufgaben
— nicht vermischen (wie im Auftrag verlangt).

---

## 5. Virtualisierung

### 5.1 Performance-Belege im Repo

- **Keine gemessene V2-Performance-Regression** gefunden — weder in
  `loop-10-teil1-2-2026-08-11.md` noch in `loop-10-teil3-2026-08-12.md` noch
  in `blocker-register.md` (B-8 betrifft visuelle Regression/Screenshots,
  nicht Renderzeit; PER-002 betrifft Multi-QAF-Budgets, nicht die Klassik-
  Tabellen).
- **Reale Zeilenzahlen-Belege** (spekulativ übertragbar, siehe 2.6):
  104/375/167/192/1.002 Zeilen aus der Vorgänger-System-Beobachtung
  (`01_QAF_Comparator_Master_Specification_v1.0.md:855-864`), plus die
  RTM-Zielschwelle „200+ rows" (`07_Requirements_Traceability_Matrix_v1.0.md:85`).
- **Kein Truncation/Pagination-Mechanismus** in den 5 Modulen oder der Schale
  gefunden (Grep auf `slice(0`, `MAX_ROWS`, `limit`, `paginat` in
  `detail-sections/*.tsx`, `qaf-comparison-detail.tsx`, `table-specs.ts` —
  0 Treffer außer einer unabhängigen `LimitsSection`, die etwas anderes meint:
  methodische Grenzen der Berechnung, nicht Zeilenbegrenzung der Tabelle).
  D. h. heute werden alle Zeilen ungekürzt gerendert — bei den Größenordnungen
  aus 2.6 (v. a. 1.002 Plausibilitätsbefunde) ist das ein plausibles
  DOM-Größenproblem, aber unbewiesen im aktuellen React-Baum.

### 5.2 Dependency-Lage — **neue Dependency nötig**

Geprüft in `package.json` (dependencies + devDependencies, vollständiger
Scan über alle Einträge): **keine** Virtualisierungs-Bibliothek vorhanden.
Kein `react-window`, kein `react-virtualized`, kein `@tanstack/react-virtual`,
kein `react-virtuoso`, kein `ag-grid`. Vorhandene tabellennahe Pakete
beschränken sich auf `@dnd-kit/core`/`@dnd-kit/sortable` (Drag-and-Drop, kein
Windowing).

**Das ist ein Blocker/Kais-Frage, kein Bau-Schritt:** CLAUDE.md nennt neue
Dependencies nicht als „frei" — jede neue Abhängigkeit ist eine Entscheidung
außerhalb des „still weiterarbeiten"-Rahmens der AUTONOMOUS-MERGE-Policy
(die deckt Branch/Commit/Push/Merge/Deploy ab, nicht Package-Auswahl). Eine
handgerollte Windowing-Lösung ohne Bibliothek wäre möglich, aber deutlich
mehr Code/Risiko (Scroll-Höhen-Messung, Tastatur-/Screenreader-Zugänglichkeit
aus Kap. 34.1 der Master Spec — „Tastaturbedienung vollständig",
„Screenreader-kompatible Tabellenüberschriften" — müsste selbst nachgebaut
werden, was eine Bibliothek mitbringt).

**Fazit Frage 5:** Virtualisierung braucht entweder eine neue Dependency
(Kais-Entscheidung nötig) oder einen spürbar größeren Eigenbau mit A11y-
Risiko. Beides sprengt den Rahmen eines „ohne neue Dependency, ohne
Scope-Klärung" fertigen ersten PRs.

---

## 6. Zuschnitt-Empfehlung

### 6.1 Ja — ein erster, in sich fertiger PR lässt sich schneiden

**Table-Engine-Konsolidierung**, ohne neue Dependency, ohne Scope-Klärung:

Die bereits existierende, getestete, pure Engine `buildTable()`/`TableRow`/
`TableFilter`/`TableSort`/`validateTable()` (`table-specs.ts`, 212 Zeilen,
18 Tests) auf die 8 heute unabhängig implementierten `<table>`-Blöcke der
Klassik-Detail-Module ausweiten (2.1, 2.3, 2.5). Das ist ein reiner
Konsolidierungsschritt der bereits vorhandenen, korrekten Logik — **kein**
Filter-UI-Design, **keine** URL-Entscheidung, **keine** Virtualisierung, und
löst real belegte Duplikation (8 Stellen mit identischem
`<table className="w-full text-sm">`-Muster, Frage 2.5).

**Schritt-Plan:**

1. `TableRow` generisch machen bzw. Adapter-Funktionen ergänzen (heute ist
   `TableRow` an `DifferenceRecord`/`DifferenceCell` gebunden, Zeile 22-40,
   22-40 — die Klassik-Zeilentypen `ProductionRateRow`/`ManufacturingDeltaRowSpec`/
   `MetricsTableRow` haben andere Felder). Zwei Optionen, beide ohne
   Verhaltensänderung der bestehenden V2-Nutzung:
   a) `TableRow` um optionale Felder erweitern, die die Klassik-Typen
      brauchen (rückwärtskompatibel, additive Erweiterung), oder
   b) einen generischen `<T>`-Renderer bauen, der Spalten-Config +
      `buildTable<T>()` als eigenständige generische Funktion nimmt, während
      die heutige `TableRow`/`buildTable()` als konkrete Instanz erhalten
      bleibt (kein Bruch der 6 V2-Aufrufer).
   Empfehlung: (b) — sauberer Schnitt, kein Risiko für die produktiv
   genutzten `mfgTable`/`matTable`-Aufrufe in `v2/page.tsz:222-245`.
2. Einen gemeinsamen `<QafDataTable>`-Renderer bauen (Server- oder reine
   Präsentationskomponente, kein `'use client'` nötig, da noch keine
   Interaktivität gefordert ist) — ersetzt die 8 handgeschriebenen
   `<table>`-Blöcke eins zu eins, Spalten als Config-Prop.
3. Migration Tabelle für Tabelle, beginnend mit der am saubersten
   abgegrenzten (`OneTimeSection`, `massnahmen-sbm-sections.tsx:256` — kleinste
   Datei laut `view-spec-adoption-analyse.md:112`, keine Explain-Panels, keine
   Maps aus der Schale), danach `ProductionSection`/`AnomaliesSection`
   (gleiche Datei, gleiches Datenmuster), zuletzt `ManufacturingDeltasSection`
   (hat Explain-Panel + Provenance-Tooltip als Sonderspalte — Config muss
   das abbilden können).
4. `A2`/`A4` (`StructureChangesSection`, `PlausibilitySection`) bewusst NICHT
   in diesem ersten PR — sie sind `<ul>`-Listen, kein `<table>`; ob sie in
   dieselbe Engine gehören oder eine eigene „Listen-Engine" brauchen, ist ein
   Zuschnitts-Folgeschritt, kein Blocker für den Tabellen-Teil.
5. Keine Filter-UI, keine Sortier-UI in diesem PR aktivieren — nur die
   Rendering-Konsolidierung. `TableFilter`/`TableSort` bleiben nutzbar, aber
   ungenutzt (wie heute), bis Frage 3 vor dem nächsten Schritt geklärt ist.

**Testplan:**

- Bestehende 18 `table-specs.test.ts`-Tests bleiben grün (keine
  Verhaltensänderung an `buildTable()`).
- Neuer Renderer-Test je migrierter Sektion: Golden-Zeilenanzahl,
  Spalten-Reihenfolge, leere Liste (Empty-State-Verhalten unverändert zur
  Vorher-Version).
- `qaf-detail-sections-parity.test.tsx` (432 Zeilen, bestehende Datei) muss
  nach jeder Migration weiter grün bleiben — das ist der bestehende
  Gegenprobe-Test „Sektion mit/ohne Daten erscheint/verschwindet korrekt"
  (`view-spec-adoption-analyse.md:143`: „kein Wert-Smoke", also zusätzlich
  ein Werte-Vergleich pro migrierter Tabelle nötig, nicht nur Vorhandensein).
- Repo-Pflichtgates vor Push: `npm run typecheck`, `npm run lint`,
  `npm run test`, `npm run build`,
  `CHECK_FORBIDDEN_LEVEL=error npm run check:portability` (aus CLAUDE.md).

**Risiken:**

- `ManufacturingDeltasSection` hat die meisten Cross-Referenzen (Explain-
  Panel, Provenance-Tooltip, 4 Lookup-Maps aus der Schale,
  `view-spec-adoption-analyse.md:130`/Aufwand-L-Einstufung für das
  Analogon in Modul 4) — höchstes Migrationsrisiko im PR, am besten zuletzt.
- Visuelle Abweichungen (Spaltenbreiten, Padding) sind über CSS-Klassen
  leicht zu reproduzieren, aber ohne visuelle Regression (B-8, nicht gebaut)
  nur durch manuellen Vergleich zu verifizieren — Restrisiko, das erst mit
  B-8 strukturell geschlossen wird.
- Der generische `<T>`-Renderer darf die bestehenden 6 V2-Aufrufe
  (`v2/page.tsx:522-595`) nicht anfassen/brechen — Empfehlung 1(b) ist genau
  deshalb so gewählt.

### 6.2 Was NICHT in diesem ersten PR geht — und was Kais entscheiden muss

**Filter-Engine/URL-State** — braucht eine Produktentscheidung, bevor Code
sinnvoll ist:

> Frage an Kais: Welche der sechs bereits im `TableFilter`-Typ vorhandenen
> Filter-Dimensionen (`matchTypes`, `buckets`, `changedOnly`, `zeroCostOnly`,
> `withFindingsOnly`, `search`) sollen als sichtbare Bedienelemente
> erscheinen — und auf welchen Seiten (nur V2-Seite, die bereits `TableSpec`
> nutzt? auch die Klassik-Schale, die die Engine noch gar nicht kennt? beide
> synchron)? Ohne diese Entscheidung gibt es keine UI, an die URL-State
> andocken könnte — „URL-State für Filter" bauen, bevor Filter-UI existiert,
> wäre Vorgriff auf eine noch offene Design-Frage.

**Virtualisierung** — braucht eine Dependency-Freigabe:

> Frage an Kais: Soll für Tabellen-Virtualisierung eine neue Dependency
> freigegeben werden (z. B. `@tanstack/react-virtual` oder `react-window` —
> beide aktuell nicht im Repo), oder soll dieser Rest-Punkt zurückgestellt
> werden, bis ein echter Performance-Befund am laufenden V2-Code vorliegt?
> Die einzigen dokumentierten Zeilenzahlen (104/375/167/192/1.002,
> Master-Spec Kap. 6.4) stammen aus einer Beobachtung des **Vorgänger-
> Systems**, nicht aus einer Messung am heutigen React-Code — es ist plausibel,
> aber nicht bewiesen, dass V2 dasselbe Problem hat.

**Evidence-Panel-Vereinheitlichung** — eigener, größerer Schnitt:

> Frage an Kais (optional, kann auch autonom entschieden werden, da keine
> „echte Produktentscheidung mit mehreren fachlich unterschiedlichen
> Ergebnissen" im engen CLAUDE.md-Sinne, aber ein Datenmodell-Entwurf mit
> Tragweite): Soll `EvidenceRow` (schmal, Zellkette) als Teilmenge in
> `ExplainAttributes` (breit, volle §17-Provenienz) aufgehen, oder bleiben
> beide getrennte Konzepte mit einem gemeinsamen Präsentations-Wrapper? Diese
> Frage entscheidet, ob die dritte Engine (Evidence) im selben PR wie Table/
> Filter behandelt werden kann — nach dieser Analyse: **nein**, eigener PR,
> weil die Datenmodelle nicht kompatibel sind, ohne dass jemand das vorher
> entscheidet.

### 6.3 Kurzfassung der Priorisierung

1. **Sofort baubar, kein Kais-Input nötig:** Table-Engine-Konsolidierung
   (6.1) — schließt reale, belegte Duplikation, macht die einzige
   existierende Engine zum einzigen Tabellen-Weg.
2. **Braucht kurze Kais-Antwort, dann baubar:** Filter-UI-Scope (welche
   Dimensionen/wo) → danach URL-State (technisch trivial, kein neues
   Package).
3. **Braucht Kais-Freigabe einer Dependency ODER Rückstellung:**
   Virtualisierung.
4. **Eigener, später Schnitt:** Evidence-Panel-Vereinheitlichung (3 Systeme
   → 1), unabhängig von 1-3 planbar, aber nicht Teil des risikoarmen ersten
   PRs.

---

## Schritt 1 UMGESETZT 15.08.2026: gemeinsamer Tabellen-Renderer (`QafDataTable`)

Der unter 6.1 empfohlene Kais-freie erste Schnitt, bewusst KLEINER als der
Maximalplan: neuer Präsentations-Baustein
`components/qaf-differences/detail-sections/qaf-data-table.tsx`
(Spalten-Config, byte-gleiches Klassen-Skelett zum bisherigen Handmuster,
exakt gepinnt im Renderer-Test) + Migration der drei risikoarmen Tabellen
(`OneTimeSection`, `ProductionSection`, `AnomaliesSection`). KEINE
Filter-/Sortier-UI aktiviert, KEINE Virtualisierung, KEINE Änderung an
`table-specs.ts` — die Engine bleibt unangetastet der Andockpunkt, sobald
die Scope-Fragen unter 6.2 beantwortet sind (an Kais gestellt 15.08.,
TG 10233).

Bewusst vertagt (Folge-PRs): `ManufacturingDeltasSection` (Explain-Panel +
Provenance-Tooltip als Sonderspalten), `FormSection`/`MetricsSection`
(zusammenfassung), Coverage-Tabelle der V2-Seite, `TableRow`-Generisierung
(Option 1b) — Letztere erst, wenn eine migrierte Tabelle wirklich
Filter/Sort braucht, nicht auf Vorrat.

## Schritt 2 UMGESETZT 15.08.2026: Form/Metrics auf `QafDataTable`

`FormSection` + `MetricsSection` migriert (Explain-Panel-Toggle als
Zellinhalt, Status-Pill als Spalten-Config, `rowClassName` additiv für die
Form-total-Hervorhebung). Verbleibende Hand-Tabellen im Scope: A1-Deltas
(`ManufacturingDeltasSection`, Sonderspalten — letzter Folge-PR) und die
V2-Coverage-Tabelle. Scope-Grenzen unverändert (keine Filter-UI, keine
Dependency, table-specs.ts unangetastet).
