# Konzeptpapier: Speicherung und Bereitstellung von Scalar- und Composite-Sportdaten

## Status

Arbeitsstand auf Basis der fachlichen und architektonischen Diskussion.  
Dieses Dokument dient als Rahmenwerk für einen Coding Agent und beschreibt die Zielarchitektur, die Abgrenzung der Schichten sowie die empfohlene technische Struktur für Scalar- und Composite-Werte.

---

## 1. Ziel und Einordnung

Das System soll zu beliebigen Sportarten Daten speichern und für unterschiedliche nachgelagerte Zwecke bereitstellen, insbesondere:

- Diagramme
- Kennzahlen
- Scores
- regelbasierte Bewertungen
- KI-Platzhalter und KI-Kontextdaten

Das System ist bereits mehrschichtig aufgebaut. Diese Schichtung ist fachlich und technisch sinnvoll und soll ausdrücklich erhalten bleiben.

### Bestehende Schichten

- **Persistenz / Datenbank**
  - reiner Wertespeicher
  - Speicherung von Scalars und Composites
- **Layer 1**
  - Lesen der Daten
  - technische Aufbereitung
  - minimale Normalisierung
  - Validierung
- **Layer 2a**
  - fachliche Aufbereitung für Diagramme, Kennzahlen, Regeln, Auswertungen
- **Layer 2b**
  - fachliche Aufbereitung für KI-Platzhalter, KI-Zusammenfassungen, KI-Argumente

### Zentrale Erkenntnis

Die Persistenz darf **nicht** mit fachlichen Interpretationen aus Layer 2 überladen werden.

Das bedeutet konkret:

- Die Datenbank speichert **Fakten und strukturierte Werte**
- Layer 1 stellt **kanonische, technische Leseobjekte** bereit
- Layer 2 erzeugt daraus **fachliche Bedeutung**

---

## 2. Problemstellung

Bisher werden überwiegend **Scalar-Werte** gespeichert:

- Datentyp
- Ausprägung (`value`)
- Einheit

Nun sollen zusätzlich **Composite-Werte** gespeichert werden.  
Aktuell ist der gewünschte Ansatz:

- derselbe Value Store
- `datatype = composite`
- `value = jsonb`

Die Herausforderung besteht darin, Composite-Werte so einzuführen, dass:

1. die Architektur einfach bleibt
2. die Schichtentrennung nicht verletzt wird
3. das System sportartenübergreifend nutzbar bleibt
4. spätere Auswertungslogik nicht bereits in die Persistenz gepresst wird
5. die Lösung für Diagramme, Kennzahlen und KI gleichermaßen nutzbar ist

---

## 3. Architektonische Leitentscheidung

### 3.1 Grundsatz

**Composite-Werte werden als strukturierte Fakten gespeichert, nicht als fachlich fertig interpretierte Analyseobjekte.**

Das bedeutet:

- Die Datenbank kennt nur wenige **technische Composite-Container**
- Fachliche Archetypen entstehen erst in Layer 2
- Layer 1 vermittelt zwischen Persistenz und fachlicher Verarbeitung

### 3.2 Was bewusst vermieden wird

Nicht in die Persistenz gehören als Teil des Basis-Composite:

- Scores
- fachliche Bewertungen
- Trainingsqualitätsurteile
- KI-Texte
- narrative Einordnungen
- Interpretationen wie „intervallartig“, „polarisiert“, „gut regeneriert“
- abgeleitete Kennzahlen wie `switch_rate`, `transition_entropy`, `readiness_index`, `plan_match_score`

Diese Dinge sind **sekundäre Ableitungen** und gehören in Layer 2.

---

## 4. Fachliche Kernaussage des Konzepts

Die frühere Diskussion über viele fachliche Composite-Archetypen war fachlich sinnvoll, aber für die Persistenz zu komplex.

Deshalb wird das Modell in zwei Ebenen getrennt:

### Ebene A: technische Speicherformen

Diese werden in der Datenbank als `jsonb` gespeichert.

### Ebene B: fachliche Analyse-Archetypen

Diese werden in Layer 2 aus den technischen Speicherformen abgeleitet.

Damit gilt:

- **wenige Speicherformen**
- **viele fachliche Projektionen**

Das ist die zentrale Designentscheidung dieses Dokuments.

---

## 5. Zielbild

### 5.1 Persistenzebene

Einheitlicher Value Store für:

- Scalars
- Composites

#### Scalar

- `datatype != composite`
- `value` ist scalar
- `unit` als separate Spalte oder Feld

#### Composite

- `datatype = composite`
- `value` ist `jsonb`
- `jsonb` folgt einem kleinen, stabilen Basisschema

### 5.2 Layer 1

Layer 1 liefert für Composite-Werte:

- validierte Daten
- minimal normalisierte Daten
- kanonische Leseobjekte
- keine fachlichen Scores oder Bewertungen

### 5.3 Layer 2a

Layer 2a erzeugt aus Layer-1-Daten:

- Diagrammserien
- Kennzahlen
- Übergangsmetriken
- Intervallstrukturen
- Auswertungsobjekte
- sportartspezifische Projektionen

### 5.4 Layer 2b

Layer 2b erzeugt aus Layer-1- oder Layer-2a-Daten:

- KI-Platzhalter
- KI-Faktenlisten
- zusammengefasste Trainingsmerkmale
- explizite, strukturierte Aussagen für Prompts

---

## 6. Technische Speicherformen für Composite-Werte

Statt vieler spezieller Composite-Typen werden nur vier technische Container eingeführt:

1. `group_set`
2. `distribution_set`
3. `sequence_set`
4. `model_set`

Diese vier Typen reichen als Speicherebene aus.

---

## 7. Der gemeinsame Basiskern jedes Composite-Objekts

Jedes Composite-JSONB soll auf einem kleinen gemeinsamen Kern basieren.

### 7.1 Basisschema

    ```json
    {
      "v": 1,
      "kind": "group_set",
      "domain": "recovery",
      "basis": null,
      "items": [],
      "meta": {
        "source": "reported",
        "definition_ref": null,
        "quality": null
      }
    }
    ```

### 7.2 Pflichtfelder

- `v`
- `kind`
- `domain`
- `items`

### 7.3 Optionale, aber empfohlene Felder

- `basis`
- `meta.source`
- `meta.definition_ref`
- `meta.quality`

### 7.4 Semantik der Felder

#### `v`

Schema-Version des gespeicherten JSON-Objekts.

#### `kind`

Technischer Composite-Container.  
Erlaubte Werte:

- `group_set`
- `distribution_set`
- `sequence_set`
- `model_set`

#### `domain`

Fachlicher Bezugsraum der Daten, zum Beispiel:

- `heart_rate`
- `power`
- `speed`
- `recovery`
- `intervals`
- `technique`
- `critical_power`

#### `basis`

Optionale Bezugsgröße, zum Beispiel:

- `time`
- `distance`
- `repetitions`
- `work`
- `count`
- `score`

#### `items`

Liste der eigentlichen Teilwerte oder Segmente.

#### `meta.source`

Ursprung des Composite-Werts, z. B.:

- `reported`
- `imported`
- `detected`
- `derived_l1`

#### `meta.definition_ref`

Referenz auf eine externe oder interne Definition, etwa:

- HF-Zonenmodell
- Power-Zonenmodell
- Intervall-Template
- Testprotokoll
- Schwellenmodell

#### `meta.quality`

Optionale Qualitätskennzeichnung, z. B.:

- `raw`
- `validated`
- `estimated`
- `reported`
- `derived`

---

## 8. Composite-Typ 1: `group_set`

### 8.1 Zweck

Speicherung einer kleinen Menge benannter Teilwerte ohne zwingende Reihenfolge und ohne Bandgrenzen.

### 8.2 Typische Einsatzfälle

- HRV-Bundle
- Wellness-/Readiness-Bundle
- Technik-Bundle
- Submetriken eines Tests
- kleine Sammlungen korrelierter Werte

### 8.3 Struktur

    ```json
    {
      "v": 1,
      "kind": "group_set",
      "domain": "recovery",
      "items": [
        { "key": "rmssd", "value": 42.1, "unit": "ms" },
        { "key": "resting_hr", "value": 54, "unit": "bpm" },
        { "key": "sleep_score", "value": 78, "unit": "score" }
      ],
      "meta": {
        "source": "reported",
        "definition_ref": "morning_readiness_protocol_v1",
        "quality": "validated"
      }
    }
    ```

### 8.4 Pflichtfelder pro Item

- `key`
- `value`

### 8.5 Optionale Item-Felder

- `unit`
- `label`
- `note`

### 8.6 Regeln

- `key` muss innerhalb eines Objekts eindeutig sein
- `value` darf scalar oder `null` sein
- keine fachliche Interpretation im Objekt selbst

### 8.7 Nicht speichern in `group_set`

Nicht in `group_set` speichern:

- `readiness_index`
- `recovery_flag`
- `trend_direction`
- generierte Scores

Diese gehören in Layer 2.

---

## 9. Composite-Typ 2: `distribution_set`

### 9.1 Zweck

Speicherung einer Verteilung über Bänder, Klassen oder Bereiche.

### 9.2 Typische Einsatzfälle

- Herzfrequenzzonen
- Power-Zonen
- Geschwindigkeitszonen
- Pace-Bänder
- Kadenz-Zonen
- Beschleunigungsbänder
- Sprunghöhenklassen

### 9.3 Struktur

    ```json
    {
      "v": 1,
      "kind": "distribution_set",
      "domain": "heart_rate",
      "basis": "time",
      "items": [
        {
          "key": "z1",
          "value": 420,
          "unit": "s",
          "lower": 50,
          "upper": 60,
          "range_unit": "%hr_max"
        },
        {
          "key": "z2",
          "value": 780,
          "unit": "s",
          "lower": 60,
          "upper": 70,
          "range_unit": "%hr_max"
        },
        {
          "key": "z3",
          "value": 360,
          "unit": "s",
          "lower": 70,
          "upper": 80,
          "range_unit": "%hr_max"
        }
      ],
      "meta": {
        "source": "reported",
        "definition_ref": "hr_zone_model_5_hrmax",
        "quality": "validated"
      }
    }
    ```

### 9.4 Pflichtfelder pro Item

- `key`
- `value`

### 9.5 Empfohlene Item-Felder

- `unit`
- `lower`
- `upper`
- `range_unit`

### 9.6 Regeln

- `key` muss eindeutig sein
- `items` sollen logisch sortierbar sein
- `lower` und `upper` sind optional, aber bei bandbasierten Sets empfohlen
- `basis` definiert, worauf sich `value` bezieht, etwa Zeit, Distanz, Wiederholungen

### 9.7 Nicht speichern in `distribution_set`

Nicht in `distribution_set` speichern:

- Wechselhäufigkeit
- mittlere Verweildauer
- Polarized Index
- Pyramidal Index
- Intervallklassifikation

Das sind Auswertungsergebnisse aus Layer 2.

---

## 10. Composite-Typ 3: `sequence_set`

### 10.1 Zweck

Speicherung geordneter Sequenzen, Segmente, Zustandsfolgen oder Intervallblöcke.

### 10.2 Typische Einsatzfälle

- Work-/Recovery-Blöcke
- erkannte HF-Zonen-Segmente
- Pace-Wechsel
- Satz-/Rundenabfolgen
- Drill-Sequenzen
- Ereignisfolgen

### 10.3 Struktur: Intervallblöcke

    ```json
    {
      "v": 1,
      "kind": "sequence_set",
      "domain": "intervals",
      "basis": "time",
      "items": [
        { "seq": 1, "label": "work", "start": 0, "duration": 180, "unit": "s" },
        { "seq": 2, "label": "recovery", "start": 180, "duration": 90, "unit": "s" },
        { "seq": 3, "label": "work", "start": 270, "duration": 180, "unit": "s" }
      ],
      "meta": {
        "source": "detected",
        "definition_ref": "interval_template_optional",
        "quality": "derived_l1"
      }
    }
    ```

### 10.4 Struktur: Zustandssegmente

    ```json
    {
      "v": 1,
      "kind": "sequence_set",
      "domain": "heart_rate_zone_state",
      "basis": "time",
      "items": [
        { "seq": 1, "label": "z2", "start": 0, "duration": 300, "unit": "s" },
        { "seq": 2, "label": "z4", "start": 300, "duration": 90, "unit": "s" },
        { "seq": 3, "label": "z2", "start": 390, "duration": 120, "unit": "s" }
      ],
      "meta": {
        "source": "detected",
        "definition_ref": "hr_zone_model_5_hrmax"
      }
    }
    ```

### 10.5 Pflichtfelder pro Item

- `seq`
- mindestens eines von:
  - `label`
  - `key`
- mindestens eines von:
  - `duration`
  - `start` und `end`

### 10.6 Optionale Item-Felder

- `unit`
- `start`
- `end`
- `value`
- `note`

### 10.7 Regeln

- `seq` muss eindeutig und aufsteigend sein
- Segmente müssen logisch geordnet sein
- bei zeitbasierten Segmenten sollen Start/Dauer-Ende konsistent sein
- keine fachlich interpretierten Kennzahlen im Objekt

### 10.8 Nicht speichern in `sequence_set`

Nicht in `sequence_set` speichern:

- `switch_count`
- `switch_rate_per_min`
- `transition_entropy`
- `density_index`
- `plan_match_score`

Diese Metriken entstehen erst in Layer 2.

---

## 11. Composite-Typ 4: `model_set`

### 11.1 Zweck

Speicherung kleiner, strukturierter Parametersätze eines Modells.

### 11.2 Typische Einsatzfälle

- Critical Power / W′
- Critical Speed
- Last-Geschwindigkeits-Profil
- Schwellenmodelle
- Testmodellparameter

### 11.3 Struktur

    ```json
    {
      "v": 1,
      "kind": "model_set",
      "domain": "critical_power",
      "items": [
        { "key": "cp", "value": 286, "unit": "W" },
        { "key": "w_prime", "value": 16800, "unit": "J" },
        { "key": "r_squared", "value": 0.98, "unit": "ratio" }
      ],
      "meta": {
        "source": "derived_l1",
        "definition_ref": "critical_power_2_parameter",
        "quality": "validated"
      }
    }
    ```

### 11.4 Pflichtfelder pro Item

- `key`
- `value`

### 11.5 Optionale Item-Felder

- `unit`
- `ci_low`
- `ci_high`
- `error`
- `note`

### 11.6 Regeln

- `key` eindeutig
- Modellname und Herleitung über `meta.definition_ref`
- Modellinterpretation nicht im Objekt selbst speichern

### 11.7 Nicht speichern in `model_set`

Nicht in `model_set` speichern:

- Trainingsdiagnosen
- Handlungsempfehlungen
- automatische Coachingtexte

---

## 12. Warum genau diese vier Typen

Diese vier Typen decken den größten Teil realistisch auftretender Composite-Fälle ab, ohne die Persistenz fachlich zu überfrachten.

### `group_set`

Für kleine Bündel benannter Werte

### `distribution_set`

Für Verteilungen über Klassen oder Bänder

### `sequence_set`

Für geordnete Folgen, Segmente und Intervallstrukturen

### `model_set`

Für modellbasierte Parametersätze

Weitere Speichertypen sollen nur eingeführt werden, wenn ein neuer Fall mit diesen vier Typen **nicht sinnvoll oder nur mit klaren Verformungen** modellierbar ist.

---

## 13. Was ausdrücklich nicht Teil der Persistenz-Basisschicht ist

Die Persistenzschicht speichert keine fachlichen Endprodukte.

Nicht Bestandteil der Basis-Composite-Struktur sind:

- Visualisierungsvorgaben
- Diagrammfarblogik
- Scoring-Ergebnisse
- Kennzahlen aus Übergangsanalysen
- narrative KI-Bausteine
- Trainingsklassifikationen
- Empfehlungen
- Bewertungen
- Interpretationen
- sportartspezifische Schlussfolgerungen

---

## 14. Layer-1-Konzept

### 14.1 Zweck von Layer 1

Layer 1 ist die technische Aufbereitungsschicht zwischen Persistenz und Fachlogik.

Layer 1 darf:

- Composite-JSON validieren
- Schema-Version prüfen
- `kind` prüfen
- Items lesen
- Einheiten normalisieren
- Basisreferenzen auflösen
- technische DTOs erzeugen
- leichte Plausibilitätsprüfungen durchführen

Layer 1 darf nicht:

- Scores berechnen
- fachliche Bewertungen erzeugen
- Intervallqualität beurteilen
- KI-Platzhalter formulieren
- Trainingscharakter interpretieren

### 14.2 Layer-1-Output

Layer 1 erzeugt kanonische interne Datenobjekte, zum Beispiel:

- `ScalarValueDTO`
- `GroupSetDTO`
- `DistributionSetDTO`
- `SequenceSetDTO`
- `ModelSetDTO`

### 14.3 Beispielhafte DTOs

    ```ts
    type ScalarValueDTO = {
      id: string
      datatype: string
      value: number | string | boolean | null
      unit?: string | null
    }

    type GroupItemDTO = {
      key: string
      value: unknown
      unit?: string | null
    }

    type GroupSetDTO = {
      id: string
      domain: string
      items: GroupItemDTO[]
      meta?: Record<string, unknown>
    }

    type DistributionItemDTO = {
      key: string
      value: number | null
      unit?: string | null
      lower?: number | null
      upper?: number | null
      rangeUnit?: string | null
    }

    type DistributionSetDTO = {
      id: string
      domain: string
      basis?: string | null
      items: DistributionItemDTO[]
      meta?: Record<string, unknown>
    }

    type SequenceItemDTO = {
      seq: number
      label?: string | null
      start?: number | null
      end?: number | null
      duration?: number | null
      unit?: string | null
      value?: unknown
    }

    type SequenceSetDTO = {
      id: string
      domain: string
      basis?: string | null
      items: SequenceItemDTO[]
      meta?: Record<string, unknown>
    }

    type ModelItemDTO = {
      key: string
      value: unknown
      unit?: string | null
    }

    type ModelSetDTO = {
      id: string
      domain: string
      items: ModelItemDTO[]
      meta?: Record<string, unknown>
    }
    ```

### 14.4 Layer-1-Validierungsregeln

#### Allgemein

- JSON muss parsebar sein
- `v` muss unterstützt sein
- `kind` muss bekannt sein
- `domain` darf nicht leer sein
- `items` muss Array sein

#### `group_set`

- jeder Eintrag braucht `key`
- `key` eindeutig

#### `distribution_set`

- jeder Eintrag braucht `key`
- `key` eindeutig
- `value` numerisch oder `null`
- `lower <= upper`, wenn beide gesetzt sind

#### `sequence_set`

- jeder Eintrag braucht `seq`
- `seq` eindeutig
- `seq` sortierbar
- zeitliche Felder konsistent

#### `model_set`

- jeder Eintrag braucht `key`
- `key` eindeutig

### 14.5 Layer-1-Normalisierungen

Layer 1 darf minimale Normalisierung durchführen:

- Sekunden, Minuten, Stunden intern vereinheitlichen
- Meter/Kilometer intern vereinheitlichen
- Prozentwerte intern konsistent repräsentieren
- Feldnamen aus Legacy-Importen normalisieren

Wichtig:  
Diese Normalisierung ist technisch, nicht fachlich interpretierend.

---

## 15. Layer-2a-Konzept: fachliche Analyse- und Diagrammprojektionen

Layer 2a konsumiert Daten aus Layer 1 und erzeugt daraus fachliche Sichtweisen.

Die früher diskutierten fachlichen Archetypen leben **hier**, nicht in der Persistenz.

### 15.1 Fachliche Archetypen in Layer 2a

1. `BandDistribution`
2. `TransitionProfile`
3. `IntervalBlockProfile`
4. `EventActionProfile`
5. `CouplingEfficiencyProfile`
6. `ModelParameterProfile`
7. `TechniqueCycleProfile`
8. `ReadinessRecoveryProfile`

### 15.2 Mapping von Speicherformen auf Layer-2a-Archetypen

#### `distribution_set`

kann projiziert werden auf:

- `BandDistribution`

#### `sequence_set`

kann projiziert werden auf:

- `TransitionProfile`
- `IntervalBlockProfile`
- `EventActionProfile`

#### `group_set`

kann projiziert werden auf:

- `TechniqueCycleProfile`
- `ReadinessRecoveryProfile`
- Teile eines `EventActionProfile`

#### `model_set`

kann projiziert werden auf:

- `ModelParameterProfile`

#### Kombination mehrerer Werte

kann projiziert werden auf:

- `CouplingEfficiencyProfile`

### 15.3 Beispiel: TransitionProfile aus `sequence_set`

Aus einer Zustandssequenz wie:

- z2 -> 300s
- z4 -> 90s
- z2 -> 120s
- z5 -> 60s

kann Layer 2a berechnen:

- Anzahl Wechsel
- Wechselrate
- mittlere Verweildauer pro Zustand
- längste Verweildauer
- Zustandsübergänge
- Intervallcharakter

Diese Werte werden **nicht** im Basis-Composite gespeichert, sondern von Layer 2a berechnet.

### 15.4 Beispiel: BandDistribution aus `distribution_set`

Aus HF-Zonen-Zeiten kann Layer 2a erzeugen:

- Diagrammserien
- Prozentanteile
- Schwerpunktzone
- Exposition oberhalb einer Schwelle
- Intensitätsverteilung

Auch dies entsteht erst in Layer 2a.

---

## 16. Layer-2b-Konzept: KI-Projektionen

Layer 2b soll keine Rohwerte direkt in Prompts kippen, sondern fachlich brauchbare Platzhalter erzeugen.

### 16.1 Grundsatz

Layer 2b konsumiert bevorzugt:

- Layer-1-Daten für einfache Fakten
- Layer-2a-Daten für fachlich verdichtete Aussagen

### 16.2 Ziel von Layer 2b

Erzeugung strukturierter KI-Bausteine wie:

- „Zeit in hoher Intensität“
- „Intervallstruktur erkannt“
- „Belastungsdichte hoch“
- „geringe Variabilität“
- „Readiness reduziert“
- „Technikmetriken auffällig“
- „intern-externe Lastkopplung stabil“

### 16.3 Beispielhafte KI-Platzhalter

    ```json
    {
      "high_intensity_time_s": 480,
      "interval_structure_detected": true,
      "switch_count": 12,
      "dominant_hr_zone": "z2",
      "readiness_state": "normal",
      "technique_variability_flag": "elevated"
    }
    ```

Wichtig:  
Auch diese Objekte sind **keine Persistenz-Basisobjekte**, sondern bereitgestellte Kontexte für KI.

---

## 17. Composite-Speicherung im bestehenden Value Store

### 17.1 Annahme über den aktuellen Store

Der bestehende Store speichert Werte als Datensätze mit:

- Datentyp
- Value
- Einheit
- weiteren Metadaten

### 17.2 Empfohlene Nutzung

#### Scalar

unverändert speichern

#### Composite

als Datensatz mit:

- `datatype = composite`
- `value = jsonb`
- `unit = null` oder optional leer, sofern Einheit innerhalb der Items gespeichert wird

### 17.3 Empfohlene Zusatzmetadaten außerhalb von `value`

Soweit euer bestehendes Modell das unterstützt, sind diese Felder außerhalb des JSONB sinnvoll:

- `id`
- `athlete_id`
- `session_id`
- `source_system`
- `measured_at`
- `datatype`
- `subtype` oder `kind` als Hilfsspalte
- `domain` als Hilfsspalte
- `created_at`
- `updated_at`

### Empfehlung

`kind` und `domain` sollten nach Möglichkeit zusätzlich indexierbar sein, selbst wenn sie primär im JSONB liegen.

---

## 18. JSONB-Schema-Regeln

### 18.1 Schema-Versionierung

Jedes Composite trägt `v`.

Regel:

- neue inkompatible Strukturen erhöhen `v`
- Layer 1 unterstützt definierte Versionen
- unbekannte Versionen werden abgelehnt oder in Fallback-Modus versetzt

### 18.2 Rückwärtskompatibilität

Wenn möglich:

- neue optionale Felder hinzufügen, ohne `v` zu erhöhen
- nur strukturelle Brüche führen zu neuer Version

### 18.3 Dokumentgröße

Composite-Objekte sollen fachlich zusammenhängend, aber nicht unnötig groß sein.

Regel:

- ein Composite speichert einen **atomaren strukturierten Sachverhalt**
- keine beliebig großen Sammelobjekte
- keine Vermischung mehrerer unabhängiger Analyseebenen

---

## 19. Definitionen, Referenzen und Kataloge

Die Basis-Composite-Objekte können Definitionen referenzieren, sollen diese aber nicht voll duplizieren.

### 19.1 Beispiele für `definition_ref`

- `hr_zone_model_5_hrmax`
- `hr_zone_model_5_hrr`
- `power_zone_model_7_ftp`
- `morning_readiness_protocol_v1`
- `critical_power_2_parameter`
- `boxing_round_structure_3x3`
- `karate_interval_template_v2`

### 19.2 Nutzen von `definition_ref`

Damit kann Layer 2:

- bandbezogene Logik verstehen
- Diagrammbeschriftungen korrekt erzeugen
- Schwellenmodelle zuordnen
- KI-Kontext korrekt ableiten

Wichtig:  
`definition_ref` verweist auf eine Definition, ersetzt diese aber nicht.

---

## 20. Abgeleitete Werte zurückspeichern

### 20.1 Grundsatz

Falls Layer-2-Ergebnisse persistiert werden sollen, sollen sie **nicht** in das Basis-Composite zurückgeschrieben werden.

Stattdessen werden sie als eigene abgeleitete Werte gespeichert.

### 20.2 Begründung

So werden vermieden:

- doppelte Wahrheit
- inkonsistente alte Berechnungsergebnisse
- Vermischung von Fakt und Interpretation
- schwierige Rebuilds

### 20.3 Empfohlenes Muster

Ein abgeleiteter Wert bekommt:

- eigenen Datensatz
- Herkunftskennzeichnung
- Regel-/Berechnungsversion
- Referenz auf Quellwerte

### 20.4 Beispiel

    ```json
    {
      "origin": "derived",
      "layer": "2a",
      "rule": "transition_profile_v2",
      "source_ids": ["value_4711", "value_4712", "value_4713"]
    }
    ```

---

## 21. Beispielhafte Ende-zu-Ende-Flows

### 21.1 Flow A: Herzfrequenzzonen

#### Persistenz

`distribution_set` mit Zonenzeiten

#### Layer 1

`DistributionSetDTO`

#### Layer 2a

- Prozentanteile
- Diagrammwerte
- dominante Zone
- Zeit oberhalb bestimmter Zonen
- Intensitätsprofil

#### Layer 2b

- KI-Platzhalter wie:
  - `dominant_hr_zone`
  - `high_intensity_time_s`
  - `intensity_distribution_type`

### 21.2 Flow B: Intervalltraining

#### Persistenz

`sequence_set` mit Work-/Recovery-Segmenten

#### Layer 1

`SequenceSetDTO`

#### Layer 2a

- Anzahl Blöcke
- Work-Recovery-Verhältnis
- mittlere Blockdauer
- Dichte
- Compliance
- Intervallcharakter

#### Layer 2b

- KI-Platzhalter wie:
  - `interval_block_count`
  - `work_recovery_ratio`
  - `interval_density_state`

### 21.3 Flow C: Readiness

#### Persistenz

`group_set` mit RMSSD, Ruhepuls, Schlafscore

#### Layer 1

`GroupSetDTO`

#### Layer 2a

- Baseline-Abweichung
- Trend
- Zustandsklassifikation

#### Layer 2b

- `readiness_state`
- `recovery_attention_flag`

### 21.4 Flow D: Critical Power

#### Persistenz

`model_set` mit `cp`, `w_prime`, `r_squared`

#### Layer 1

`ModelSetDTO`

#### Layer 2a

- Modellgüte
- Vergleich zum Verlauf
- Zonen-/Schwellenableitung

#### Layer 2b

- KI-konforme Modellzusammenfassung

---

## 22. Regeln zur Entscheidung: Scalar oder Composite

### 22.1 Scalar verwenden, wenn

- genau ein Wert gespeichert wird
- keine strukturierte Untergliederung nötig ist
- keine zusammenhängende Teilwertgruppe existiert

Beispiele:

- Durchschnittspuls
- Maximalpuls
- Distanz
- Dauer
- Durchschnittsleistung

### 22.2 Composite verwenden, wenn

- mehrere Teilwerte logisch zusammengehören
- die Teilwerte nur gemeinsam sinnvoll interpretierbar sind
- Reihenfolge, Verteilung oder Modellstruktur relevant ist

Beispiele:

- HF-Zonen
- Intervallblöcke
- Readiness-Bundle
- Critical-Power-Parameter

### 22.3 Nicht jedes Mehrfachfeld ist automatisch Composite

Wenn mehrere Werte unabhängig voneinander existieren und auch einzeln sinnvoll sind, sollen sie weiterhin als Scalars gespeichert werden.

Composite nur dann verwenden, wenn die Gruppe selbst eine eigenständige semantische Einheit ist.

---

## 23. Regeln zur Einführung neuer Composite-Formate

Ein neues Composite-Format darf nur eingeführt werden, wenn mindestens eine der folgenden Bedingungen erfüllt ist:

1. Der neue Fall lässt sich mit den vier Basis-Typen nicht sinnvoll ausdrücken
2. Eine Abbildung wäre nur durch unsaubere oder missbräuchliche Nutzung möglich
3. Die Lesbarkeit und Wartbarkeit würden mit den vorhandenen Typen stark leiden

Vor Einführung eines neuen Typs ist zu prüfen:

- Kann es als `group_set` modelliert werden?
- Kann es als `distribution_set` modelliert werden?
- Kann es als `sequence_set` modelliert werden?
- Kann es als `model_set` modelliert werden?

Nur wenn alle vier Antworten fachlich und technisch nicht tragfähig sind, soll ein neuer Speichertyp diskutiert werden.

---

## 24. Implementierungsrichtlinien

### 24.1 Parser

Implementiere einen zentralen Composite-Parser:

- Eingang: Value-Record mit `datatype = composite`
- Ausgabe: konkretes Layer-1-DTO je `kind`

### 24.2 Validator

Implementiere einen zentralen Validator:

- allgemeine Prüfung
- kind-spezifische Prüfung
- strukturierte Fehlermeldungen

### 24.3 Mapper

Implementiere Mapper:

- `CompositeRecord -> GroupSetDTO`
- `CompositeRecord -> DistributionSetDTO`
- `CompositeRecord -> SequenceSetDTO`
- `CompositeRecord -> ModelSetDTO`

### 24.4 Projection Services in Layer 2

Beispiele:

- `BandDistributionProjectionService`
- `TransitionProfileService`
- `IntervalAnalysisService`
- `ReadinessProjectionService`
- `CouplingAnalysisService`
- `TechniqueProjectionService`

### 24.5 KI-Mapping in Layer 2b

Beispiele:

- `AiPlaceholderBuilder`
- `AiFactsBuilder`
- `AiNarrativeInputBuilder`

---

## 25. Fehler- und Fallback-Strategie

### 25.1 Ungültiges Composite

Wenn ein Composite ungültig ist:

- Layer 1 liefert strukturierten Fehler
- der Datensatz wird nicht als fachlich gültig weitergereicht
- optional kann ein „raw passthrough“ für Debugging existieren

### 25.2 Unbekannte Version

Wenn `v` unbekannt ist:

- Datensatz markieren
- nicht stillschweigend interpretieren
- optional Migration oder Fallback-Parser nutzen

### 25.3 Teilweise fehlende Felder

Wenn optionale Felder fehlen:

- lesen, sofern Kernschema gültig bleibt
- fehlende Informationen in Layer 2 berücksichtigen
- keine stillschweigende Erfindung fachlicher Werte

---

## 26. Performance- und Wartungsaspekte

### 26.1 Grundsatz

Die Persistenz soll flexibel, aber strukturiert bleiben.

### 26.2 Empfehlungen

- kleine, atomare Composite-Objekte
- keine übergroßen Sammelcontainer
- `kind` und `domain` indexierbar halten
- Definitionen referenzieren statt duplizieren
- fachliche Projektionen nicht als Pflichtbestandteil der Persistenz speichern

### 26.3 Vorteil dieser Struktur

- geringe Kopplung
- stabile Persistenz
- evolvierbare Fachlogik
- gute Eignung für mehrere Sportarten
- gute Wiederverwendbarkeit in Layer 2a und 2b

---

## 27. Migrationsstrategie

Falls bereits Composite-Ansätze existieren, wird folgendes Vorgehen empfohlen:

### Phase 1

- Einführung der vier `kind`-Typen
- Aufbau von Validator und Parser
- Speicherung neuer Composites nach neuem Muster

### Phase 2

- Layer-1-DTOs einführen
- bestehende Reader umstellen

### Phase 3

- Layer-2a-Projektionen implementieren
- Diagramme und Kennzahlen umstellen

### Phase 4

- Layer-2b-KI-Platzhalter auf neue Projektionen aufsetzen

### Phase 5

- bestehende Altformate optional migrieren oder kompatibel lesen

---

## 28. Nicht-Ziele dieses Dokuments

Dieses Dokument definiert bewusst nicht:

- konkrete UI-Diagrammgestaltung
- konkrete Score-Formeln
- sportartspezifische Bewertungslogiken
- konkrete KI-Prompttexte
- konkrete Datenbanktabellenmigrationen im Detail
- konkrete API-Endpunkte

Diese Punkte sind nachgelagerte Spezifikationen.

---

## 29. Offene Erweiterungspunkte

Die Architektur lässt folgende spätere Erweiterungen zu, ohne die Persistenzbasis zu brechen:

- zusätzliche `definition_ref`-Kataloge
- sportartspezifische Layer-2-Projektionen
- neue KI-Platzhaltertypen
- zusätzliche Qualitäts- und Provenienzfelder
- Rückspeicherung abgeleiteter Werte als eigene Datensätze
- spezielle Event-Detektion in Layer 2a

---

## 30. Endgültige Entscheidung

### Die Persistenzschicht speichert:

- Scalars
- vier einfache technische Composite-Typen

### Layer 1 übernimmt:

- Lesen
- Validieren
- minimale Normalisierung
- DTO-Bildung

### Layer 2a übernimmt:

- fachliche Analyse
- Diagrammaufbereitung
- Kennzahlen
- Profile
- Scores
- Interpretationen

### Layer 2b übernimmt:

- KI-Platzhalter
- KI-Fakten
- KI-Zusammenfassungen
- KI-taugliche Verdichtungen

---

## 31. Kurzfassung für einen Coding Agent

### Ziel

Erweitere den bestehenden Value Store um Composite-Werte auf `jsonb`-Basis, ohne fachliche Analyse-Logik in die Persistenz zu verlagern.

### Implementiere

1. Unterstützung für `datatype = composite`
2. vier `kind`-Typen:
   - `group_set`
   - `distribution_set`
   - `sequence_set`
   - `model_set`
3. Layer-1-Validatoren und Parser
4. kanonische DTOs pro `kind`
5. klare Trennung:
   - Persistenz = strukturierte Fakten
   - Layer 1 = technische Aufbereitung
   - Layer 2a = fachliche Projektionen
   - Layer 2b = KI-Projektionen

### Vermeide

- Scores im Basis-Composite
- Interpretationen im Basis-Composite
- Diagramm- oder KI-Logik in der Persistenz
- zu viele spezialisierte Composite-Speichertypen

---

## 32. Akzeptanzkriterien

Die Umsetzung gilt als fachlich passend, wenn:

1. ein Composite-Value mit `jsonb` gespeichert werden kann
2. Layer 1 den Typ sicher validieren und lesen kann
3. die Persistenz keine fachlichen Layer-2-Interpretationen erzwingt
4. HF-Zonen als `distribution_set` abbildbar sind
5. Intervallblöcke als `sequence_set` abbildbar sind
6. Readiness-Bundles als `group_set` abbildbar sind
7. Modellparameter als `model_set` abbildbar sind
8. Layer 2a daraus fachliche Projektionen erzeugen kann
9. Layer 2b daraus KI-Platzhalter erzeugen kann
10. neue Sportarten ohne neue Persistenzarchitektur integrierbar sind

---

## 33. Schlussformel

Die Leitentscheidung dieses Konzepts lautet:

**Die Datenbank speichert Struktur.  
Layer 1 liefert kanonische technische Objekte.  
Layer 2 erzeugt fachliche Bedeutung.  
KI konsumiert diese Bedeutung, nicht die rohe Persistenzstruktur.**

Damit bleibt das System einfach genug für die Implementierung und offen genug für spätere sportartspezifische Erweiterungen.