5) Nächster Schritt: was als Nächstes testen/bauen?

Abdeckung messen (make coverage-html) und gezielt Lücken schließen.

Kleine Ergänzungs-Tests (nur wenn dir noch was fehlt):

CSV + Excel Pfade gleich behandelt (flatten→lift) – ein Cross-check Test.

Helper-Spalten droppen bei unpack (haben wir indirekt, aber ein direkter Test ist nett).


Fehlerfälle: FK zeigt auf nicht existierendes Blatt (derzeit ignorieren wir; für 2b Validation relevant).

Dann Phase 2b: Validation & Flags
Wir führen ein zentrales validate.py ein, plus CLI-Flags:

--fail-on-missing-fk / --warn-on-missing-fk

Report pro Blatt (Counts + kleine Stichprobe)

Tests für Fehlersituationen

Das alles hängt jetzt sauber an der Engine und bleibt von Excel/CSV entkoppelt – guter Schritt Richtung wartbarer Architektur.


0000000000000000000000000000000

[200~Roadmap-Reminder (wo wir im Plan stehen)

✅ Phase 1: Multi-Sheet Orchestrierung, flatten↔lift, Pack/Unpack.

✅ Phase 2a: FKs + statische Helper-Spalten; Klammer-Konvention; Typ-Normalisierung.

⏭ Phase 2b: Validierung & Optionen

--fail-on-missing-fk / --warn-on-missing-fk

saubere Fehlermeldungen, evtl. Report pro Sheet.

⏭ Phase 2c: Pro-Sheet Overrides im YAML (id_field, label_field, detect_fk, helper_prefix, etc.)

⏭ Phase 3: Excel-Komfort

VLOOKUP/XLOOKUP statt statischem Text (damit Name „live“ folgt)

Dropdown/Data Validation für FK-Spalten

dezentes Styling/Color Coding (Header, FK, Helper readonly).

⏭ Phase 4: Echte mehrzeilige Header („split“-Modus).

⏭ Phase 5: Doku & Beispiele, carve-out in eigenes Repo, Releases.

Wenn du die Tests einspielst und alles grün bleibt, gehen wir direkt in Phase 2b (Validierung & Flags) über.

-----------------------------------

Zwei Abbildungen im Spreadsheet (pro Liste konfigurierbar)

inline-delimited (eine Zelle, Trennzeichen)

tags → Spalte tags mit Inhalt rot | blau (Delimiter konfigurierbar, Escape \).

Für FK-Listen: id_(Kategorien) mit mehreren IDs in einer Zelle (z. B. A | B | C). Helper: _Kategorien_names mit Bücher | Filme | ….

Pro: schnell editierbar, wenig Blätter; Contra: Formeln/Validierung komplizierter.

child-sheet (normiert, eigenes Blatt)

Blattname: Bestellungen.positionen (oder Bestellungen__positionen), Spalten: parent_id, pos (Reihenfolge), Felder der List-Objekte.

Für FK-Listen: eine Spalte id_(Kategorien) + Helper _Kategorien_name.

Pro: sauber, gut validierbar, gut filterbar; Contra: mehr Blätter.

Default: inline-delimited für Primitives & FK-Listen, child-sheet für Objekt-Listen.

---------------------------------------------


6ff

[200~Das passt super zu unserem bisherigen Design – deine Zusammenfassung baut logisch drauf auf. 💯
Kurz: Wir können das als Phase 6ff übernehmen, und es beißt sich nicht mit 2b–5. Im Gegenteil: 2b (Validierung) & 2c (YAML-Overrides) sind perfekte Vorarbeit. Excel-Goodies (Phase 3), split-Header (Phase 4) und Doku (Phase 5) bleiben unabhängig.

Hier ist, wie ich’s mit unseren bestehenden Konventionen zusammenführe und was wir minimal anpassen sollten.

Wo es andockt (und 2 kleine, notwendige Anpassungen)

FK-Klammerkonvention weiterverwenden – überall
Alle Referenzen bleiben im Format
"<key_field>_(<SheetKey>)"
– also auch für Eltern, Junctions, und tiefe Ebenen.

Eltern-FK in Kind-Sheet: id_(Kunden) (oder Schluessel_(Kunden)).

Junction: id_(Kunden), id_(Tags).

Enkel (Kind-Kind): line_id_(Kunden_adressen).

Regex anpassen, damit line_id erlaubt ist
Unser aktuelles FK_PATTERN schließt _ in <key_field> aus (Autsch).
Ändern auf: ^(?P<id_field>[A-Za-z0-9_]+)_\((?P<sheet_key>[^)]+)\)$
→ dann matcht auch line_id_(...).

SheetKey-Normalisierung erweitern
Bisher: Leerzeichen → _.
Neu: auch . → _ (für Kunden.adressen → Kunden_adressen).
So bleiben Schlüssel stabil und kollisionsarm.

Kanonische Tabellenformen (leichtgewichtiges ORM)
Eltern

Sheet: Kunden

Pflichtspalten: id (oder konfigurierbar), name (Label)

Alles andere: normale Felder.

1:n (Kind-Tabellen)

Sheet: Kunden.adressen

Pflicht:

line_id (stabil, eindeutig je Kindzeile)

id_(Kunden) (Eltern-FK; per YAML auch Schluessel_(Kunden))

idx (Integer; Reihenfolge innerhalb des Elterns)

Optionale Felder: type (Enum), weitere Felder.

Helper: _Kunden_name (auto, nur Anzeige)

n:m (Junction)

Vokabular: Tags (id, name, …)

Junction-Sheet: Kunden_tags (oder Kunden.tags – ich würde Kunden_tags nehmen)

id_(Kunden), id_(Tags), optional idx

Helper: _Kunden_name, _Tags_name (auto)

Liste-in-Liste (Enkel)

Sheet: Kunden.adressen.addons

Pflicht:

line_id (eigene ID der Enkelzeile)

line_id_(Kunden_adressen) (FK auf die Kind-Zeile)

idx

Optional type etc.

Helper: _Kunden_adressen_<etwas> möglich, wenn sinnvoll.

Reihenfolge: immer über idx (deterministisch, stabil).
Typisierung/Varianten: über type (Enum), gern via Excel-Dropdowns validierbar (Phase 3).

JSON-↔-Spreadsheet Roundtrip (Implementationsskizze)
Pack (JSON → Sheets)

Kind-Listen: erzeugen Kunden.adressen mit Spalten line_id, id_(Kunden), idx, …

Falls line_id im JSON fehlt: deterministisch erzeugen (z. B. "{parent_id}:{idx}" oder UUID v4 – konfigurierbar; deterministisch ist angenehmer für Diffs).

Enkel: analog mit line_id_(Kunden_adressen).

n:m: aus customer.tags (IDs) Junction-Zeilen erzeugen.

Helper-Spalten: für jede FK-Spalte _…_name auffüllen (wie wir’s jetzt schon tun).

Unpack (Sheets → JSON)

Kind: pro id_(Kunden) gruppieren, nach idx sortieren, Liste bauen; line_id mitnehmen.

Enkel: pro line_id_(Kunden_adressen) gruppieren, nach idx sortieren, als Nested-Liste unter die richtige Kind-Zeile einhängen.

n:m: pro id_(Kunden) sammeln → wahlweise als ID-Liste oder expandierte Objekte (YAML-Flag).

Helper-Spalten: wie gehabt droppen (Prefix _).

Validierung (Phase 2b): fehlende Ziel-IDs, verwaiste line_id_(…), doppelte line_id/idx → warn/fail je Flag.

YAML-Erweiterung (nur Skizze)
defaults:
  id_field: id
  label_field: name
  helper_prefix: "_"
  detect_fk: true
  list_defaults:
    mode: child-sheet        # child-sheet | inline-delimited
    delimiter: " | "
    generate_line_id: "deterministic"  # deterministic | uuid
sheets:
  - name: Kunden
    json: data/kunden.json
    lists:
      - path: adressen[]             # Objekt-Liste
        mode: child-sheet
        child_sheet: Kunden.adressen
        parent_fk: "id_(Kunden)"     # Standard; erlaubt auch Alias "parent_id"
        order_field: idx
        line_id_field: line_id
      - path: tags[]                 # n:m über Junction
        mode: junction
        vocab_sheet: Tags
        junction_sheet: Kunden_tags
        parent_fk: "id_(Kunden)"
        vocab_fk: "id_(Tags)"
        as: ids                      # ids | objects

Was müssen wir im Code anpassen/ergänzen?

FK-Engine

Regex wie oben anpassen.

normalize_sheet_key: auch . → _.

Eltern-FKs funktionieren sofort (wir behandeln sie wie jede FK-Spalte).

List-Compiler (neu)

json_lists -> child sheets: erzeugt Frames für Parent.field/junction.

child sheets -> json_lists: gruppiert zurück.

Für Enkel die Kette via line_id_(Parent_field) auflösen.

Validierung (Phase 2b)

Fehlende FK-Ziele: warn/fail.

Doppelte line_id in denselben Sheet(s): fail.

idx-Lücken sind ok, aber idx doppelt im selben Parent → warn/fail.

Junction ohne Vokabular-ID → warn/fail.

Orphan-Enkel (ohne existierende Kind-line_id) → fail.

Tests (mindestens)

1:n Roundtrip mit line_id, idx.

Liste-in-Liste Roundtrip mit line_id_(…).

n:m Roundtrip; wahlweise als ID-Liste oder expandiert.

Mischtyp-IDs (str/int) – unsere Normalisierung deckt das schon.

YAML-Overrides (andere id_field, line_id_field).

Wechselwirkungen mit 2b–5

2b Validierung: direkte Grundlage – bauen wir jetzt und nutzen’s für Listen (fehlende Eltern, Orphans, Duplikate).

2c YAML: Listen brauchen Pfad-Konfigs → 2c zuerst.

3 Excel: XLOOKUP/TextJoin & Dropdowns werden bei Listen hübsch, sind aber optional.

4 split-Header: unabhängig. Child-Sheets haben klare Spaltennamen, inline-delimited bleibt 1-zeilig.

5 Doku: Listen werden ein eigenes Kapitel.

Mini-Delta, das wir sofort ziehen sollten

FK_PATTERN → ^(?P<id_field>[A-Za-z0-9_]+)_\((?P<sheet_key>[^)]+)\)$

normalize_sheet_key → Leerzeichen und Punkte zu _

Wenn du möchtest, mache ich als Nächstes 2b (Validierung & Flags) startklar (kleiner, fokussierter PR):

--fail-on-missing-fk/--warn-on-missing-fk

Report je Blatt (Counts/Beispiele)

Tests für die wichtigsten Fehlerfälle.

Danach 2c (YAML Pfad-Overrides) und dann Phase 6 (Listen) – damit bleiben wir sauber inkrementell.















