= SH-003: Transform — Kreuztabelle → XRef (Long-Form)
:status: Proposed
:owner: Core lib team
:priority: High
:target_version: 0.1.0bX
:created: 2025-09-26

== Context
Fachliche Regeln liegen teils als Kreuztabellen vor (Matrix mit Zeilen-Keys × Spalten-Keys, Zellen als Marker). Daraus soll eine relationale XRef-Tabelle (long-form) erzeugt werden. Über den Spalten der Matrix können 1..n Zusatz-Bedingungszeilen stehen, die pro Spalte gelten.

== Goals
*G1.* BoundStep-Factory bereitstellen, die eine Kreuztabelle in eine XRef-Tabelle normalisiert.
*G2.* Zusatzbedingungen oberhalb der Matrix (Header-Zeilen) pro Spalte mitführen.
*G3.* Marker/Leerwert konfigurierbar (z. B. „X“, 1/0, nicht-leer).

== Acceptance Criteria
- AC1: Factory `make_crosstab_to_xref_step` (Frames→Frames) mit Config:

----
{
"sheet": "rules_matrix",
"row_key_cols": ["variable_id"], # links stehende Key-Spalten
"col_key_from": "columns", # Spaltennamen als Schlüssel
"header_rows": 1, # Anzahl Zusatzzeilen oberhalb der Matrix (0..n)
"value_as": "present", # Name der bool/marker-Spalte im Output
"marker_values": ["X", 1, true], # Werte, die als 'present' gelten
"drop_false": true # Zeilen ohne Marker verwerfen
}
----

- AC2: Output-Sheetname konfigurierbar (Default: `<sheet>_xref`).
- AC3: Mehrere Zusatzzeilen werden als zusätzliche Output-Spalten ausgegeben (z. B. `cond_1`, `cond_2`, …) oder über optionale `header_aliases`.
- AC4: Robust bei leeren Zeilen/Spalten und Merge-Zellen (soweit vom Reader unterstützt; andernfalls klares Fehlersignal).
- AC5: Unit-Tests mit Mini-Beispielen (inkl. 0/1/X-Marker, 0/2/3 Headerzeilen).

== Notes (Implementation)
- Intern `melt`/`stack`-ähnliche Transformation auf DataFrame-Ebene; Headerzeilen via separatem Frame/Offset einlesen und pro Spalte joinen.
- Keine Datei-IO im Step; Persistenz durch Runner/Adapter.
