# Trainingsrahmenprogramm — Technische Spezifikation

**Status:** Rahmen‑Bibliothek + Slot‑Blueprint dokumentiert · **Stand:** 2026-05-05 (Migration **036–037**)  
**Bindendes Fachkonzept / Entscheide:** `.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` (CURR‑001 bis CURR‑013)

**Relevant für nächsten Schritt:** CURR‑002 **(2)** Trainingsplanung / Rahmen über mehrere Einheiten — der hier dokumentierte **Progressionsgraph Stufe 1** ist bewusst **unterstützend**, keine Pflicht für Slot-Zuordnungen (**CURR‑013**).

---

## 1. Abgrenzung zu anderen Dokumenten

| Dokument | Rolle · warum **nicht** hier hineinmischen |
|----------|--------------------------------------------|
| `EXERCISES_DATABASE_FINAL.md`, `EXERCISES_ARCHITECTURE.md`, `EXERCISES_API_SPEC.md` | **Übungskatalog** inkl. Varianten-Progression **innerhalb einer Übung** (Migration 014). Kanten **zwischen** Übungen siehe **§3**. |
| `DATABASE_SCHEMA.md` | **Nachgeordnete** Übersicht: Migrationshistorie und Tabellenliste; Detail-DDL primär **hier §2–§3** + SQL unter `backend/migrations/`. |
| `functional/DOMAIN_MODEL.md` | Fachliche Begriffe; Kurzverweis auf Progressionsgraph ergänzt. |
| `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` | **Was** und **warum** (Bibliothek vs. Instanz, Governance, CURR‑Tabelle). |
| `functional/PARALLEL_TRAINING_STREAMS_CONCEPT.md`, `technical/PARALLEL_TRAINING_STREAMS_SPEC.md` | **Parallele Streams / Breakout innerhalb einer Einheit** — orthogonale Domäne zu **Rahmen‑Slots** (Serien‑Sessions). |
| `technical/SKILL_SCORING_SPEC.md` | **Fähigkeiten-Profil** der Rahmen‑Slots / Module / Pfade; Listen-Filter und Peer‑Vergleich (nur gleicher Artefakttyp). |

**Konsequenz:** Diese Datei bleibt der **technische Arbeitspool** für Rahmenprogramm Stufe 1–2. Abschnitt **§4** beschreibt explizit den **aktuellen Produktfreigabe-Umfang** und **bekannte Lücken** (damit Trainingsplanung weiter gebaut werden kann ohne falscher Erwartung an „Alternative‑Pakete“ in der UI).

---

## 2. Rahmenprogramm (CURR‑002 Stufe 2) — Checkliste & technische Ausarbeitung

### 2.0 Technische Entscheidung: nur Bibliothek + Slot‑Blueprint = `training_units`

**Fachlich:** Das Rahmenprogramm ist eine **wiederverwendbare Bibliotheksvorlage** ohne Bindung an eine Trainingsgruppe oder einen Kalendertermin (**Migration 036** entfernte `plan_mode`, `group_id` am Kopf sowie die Nutzung von `training_framework_slots.training_unit_id` für „konkrete“ Kopplung).

**Slot‑Inhalt (Migration 037):** Pro `training_framework_slot` existiert genau eine **Blueprint‑Zeile** in `training_units` mit **`framework_slot_id`** (partieller **UNIQUE**-Index); der Ablauf entspricht **derselben** Struktur wie geplante Einheiten über **`training_unit_sections`** und **`training_unit_section_items`** (Übungen, Notizen, Varianten wie in der Planung). Die frühere Tabelle **`training_framework_slot_exercises`** wird nach Datenübernahme **`DROP`**pt.

**Geplante Einheit aus Rahmen:** **`POST /api/training-units/from-framework-slot`** kopiert diese Blueprint‑Unit (**tiefe Kopie**) mit **`group_id` + `planned_date`**; **`origin_framework_slot_id`** hält die Herkunft (Lineage‑Light). **`GET /api/training-units`** blendet Einheiten mit **`framework_slot_id IS NOT NULL`** aus (Kalender/API‑Liste ohne Rahmen‑Blueprints).

**CHECK‑Constraint auf `training_units`:** Zeile ist entweder **Blueprint** (`framework_slot_id` gesetzt, `group_id`/`planned_date` NULL, kein `origin_framework_slot_id`) oder **Kalender‑Einheit** (`framework_slot_id` NULL, `group_id` und `planned_date` gesetzt; `origin_framework_slot_id` optional).

**Konsequenz Konzept CURR‑012 („concrete/library“):** Persistiert wird **ein** Kopf ohne Modus-Spalte: Immer Bibliotheks‑Rolle; Konkretisierung nur über Planung/API‑Kopie. Historische DDL mit `plan_mode` siehe **`035`**/`036` in dieser Datei (**§5 Changelog**) und `backend/migrations/`.

### 2.1 Checkliste (Abhak-Stand)

- [x] **Entität(en):** `training_framework_programs` (**CURR‑009**); `training_plan_templates` unverändert **eine‑Einheit‑Mikrovorlage** (**C5**).
- [x] **Bibliothek only (036):** Kopf ohne `plan_mode`/`group_id`; Kontextfilter **`focus_area_id`**, **`style_direction_id`**; M:N **`training_framework_program_training_types`**, **`training_framework_program_target_groups`**.
- [x] **Zielliste:** `training_framework_goals`, API **≥ 1** Ziel (**CURR‑011**).
- [x] **Slots:** `training_framework_slots` mit **`sort_order`**, optional **Titel/Notizen**; **Ablauf** über zugehörige **Blueprint‑`training_units`** + Sektionen/Items (**037**), nicht mehr `training_framework_slot_exercises`.
- [x] **Progressionsgraph:** Stufe 1 (**§3–§4**); **kein Pflichtbezug** pro Slot (**CURR‑013**).
- [x] **Kein Live‑Write** von Kalendereinheiten zurück in die Vorlage (**CURR‑006**); Konkretisierung = **Kopie** (siehe **§2.4**).
- [x] **Instanziierung (MVP):** `POST /api/training-units/from-framework-slot` — weiterer Ausbau: Bulk, Kalender‑UI‑Flow, **`training_plan_template_id` pro Slot** weiterhin optional/deferred (**CURR‑010**).
- [x] **Governance:** `visibility`, `club_id`, **`training_plan_templates.visibility`** (**035**) — (**CURR‑005–008**).
- [x] **REST Rahmenprogramm:** `/api/training-framework-programs` (**§2.3**); Planung (**§2.4**).

### 2.2 DDL‑Überblick (Migrationen **035** → **036** → **037**)

Auszug aktueller Zustand nach **036/037** (Details: `backend/migrations/035_training_framework_programs.sql`, `036_framework_program_context_only_library.sql`, `037_training_framework_blueprint_units.sql`):

```sql
-- Kopf (ohne Modus-Spalten nach 036)
training_framework_programs (
  id, title NOT NULL, description,
  planned_period_start, planned_period_end NULL,
  visibility NOT NULL, club_id, created_by,
  focus_area_id, style_direction_id NULL REFERENCES …,
  … timestamps
)

training_framework_goals (
  id, framework_program_id FK CASCADE, sort_order UNIQUE per framework,
  title, notes
)

training_framework_slots (
  id, framework_program_id FK CASCADE, sort_order UNIQUE per framework,
  title, notes,
  training_unit_id FK … (Spalte technisch noch vorhanden; fachlich ungenutzt, per 036 geleert)
)

-- Blueprint: eigene Einheit wie in der Planung (031)
training_units.framework_slot_id  -- UNIQUE (partial index), FK → slots ON DELETE CASCADE
training_units.origin_framework_slot_id  -- optional auf Kopien aus dem Rahmen (SET NULL)
-- CHECK chk_training_units_blueprint_vs_scheduled (Blueprint vs. Kalender‑Zeile)
-- training_framework_slot_exercises → entfallen (037)
```

**Löschkaskaden:** Rahmen löschen ⇒ Ziele + Slots; **Slot löschen** ⇒ zugehörige Blueprint‑`training_unit` per **`ON DELETE CASCADE`** auf `framework_slot_id`.

### 2.3 REST‑Überblick (`router` `training_framework_programs`)

| Methode | Pfad | Zweck |
|---------|------|--------|
| GET | `/training-framework-programs` | Liste; Admin/Superadmin alle, sonst eigene (`created_by`); u. a. `goals_count`, `slots_count`, Kontext‑Counts |
| GET | `/training-framework-programs/{id}` | Detail inkl. `goals[]`, `slots[]` mit je **`blueprint_training_unit_id`**, **`sections[]`**, **`exercises[]`** (letzteres aus Sektionen geflacht, kompatibel zum Editor) |
| POST | `/training-framework-programs` | Neu; **Pflicht:** `title`, **`goals`** (≥ 1); optional `slots` (weiterhin **`exercises[]`** pro Slot möglich — Backend materialisiert Sektionen); Header: `focus_area_id`, `style_direction_id`, `training_type_ids`, `target_group_ids`, … |
| PUT | `/training-framework-programs/{id}` | Header; volles Ersetzen von **`goals`** und/oder **`slots`** (neue Slots ⇒ neue Blueprint‑Units) |
| DELETE | `/training-framework-programs/{id}` | Rahmen + Kinder |

**AuthZ:** wie zuvor — Planungsrolle zum Schreiben; Lesen/Schreiben/Löschen des Rahmens: Admin/Superadmin oder Ersteller.

**Payload‑Hinweise (JSON), Slots:**

- Weiterhin: `exercises: [{ exercise_id, exercise_variant_id?, order_index? }]` (wird intern in eine Sektion übernommen).
- Erweitert möglich: `sections` wie bei **`PUT /api/training-units/{id}`** (voller Ablauf mit Notizen/Items).

### 2.4 Planung / Kalender (`router` `training_planning.py`, Auszug)

| Methode | Pfad | Zweck |
|---------|------|--------|
| GET | `/training-units` | Nur **Kalender‑Einheiten** (`framework_slot_id IS NULL`) |
| GET/PUT | `/training-units/{id}` | Blueprint lesen/bearbeiten: möglich mit Rahmen‑Auth; spezielle Regeln im **PUT** (kein Template‑Reset, kein `plan_template_id` am Blueprint) |
| DELETE | `/training-units/{id}` | Blueprint **nicht** über diesen Pfad löschen (Fehlerhinweis); Slot entfernen über Rahmen‑**PUT** |
| POST | `/training-units/from-framework-slot` | Body: `framework_slot_id`, `group_id`, `planned_date` — tiefe Kopie + **`origin_framework_slot_id`** |

**Frontend:** `createTrainingUnitFromFrameworkSlot` in `frontend/src/utils/api.js`.

---

## 3. Progressionsgraph Übung → Übung (implementierter Stand)

### 3.1 Abgrenzung

- **Zwischen Übungen:** gerichtete Kanten auf Ebene **`exercises`** mit optionalen Endpunkten auf konkreten **`exercise_variants`** (Knoten = „Übung“ oder „Übung · Variante“). Migrationen **032–034**.
- **Innerhalb einer Übung:** Reihenfolge / Progressionsmetadaten der Varianten unverändert über **`exercise_variants`** (Migration **014**) — nicht duplizieren.

AuthZ analog **`training_plan_templates`**: Graph nur für **Admin/Superadmin** oder **Ersteller** (`created_by`); Anlegen neuer Graphen mit **`_has_planning_role`**.

### 3.2 Migrationen & Schema (Kurz)

| Mig. | Inhalt |
|------|--------|
| **032** | `exercise_progression_graphs` (Name, Beschreibung, **`visibility`**, **`club_id`**, **`created_by`**); `exercise_progression_edges` (`graph_id`, von/nach Übung, `edge_type` VARCHAR Default `next_exercise`). FK CASCADE zu Graph und Übungen. |
| **033** | `exercise_progression_edges.notes` (freier Text / „Entwicklungsziel“ pro Kante). |
| **034** | `from_exercise_variant_id`, `to_exercise_variant_id` (nullable, FK `exercise_variants`, CASCADE). CHECK: gleiche Übung nur mit **zwei verschiedenen Varianten**. UNIQUE-Index über Graph + Endpunkte inkl. `COALESCE(variant_id,0)` + `edge_type`. |

Kantentypen in Produktnutzung: **`next_exercise`** (Nachfolger), **`sibling`** (Schwester / gleiche „Entwicklungslage“, semantisch oft Paar — weiterhin eine gerichtete Kante in DB).

Listenqueries liefern Join‑Felder **`from_exercise_title`**, **`to_exercise_title`**, **`from_variant_name`**, **`to_variant_name`**.

### 3.3 REST (`/api`, Router `exercise_progression_graphs.py`)

| Methode | Pfad | Zweck |
|---------|------|--------|
| GET | `/exercise-progression-graphs` | Liste (+ `edges_count`); Admin sieht alle, sonst nur eigene. |
| GET | `/exercise-progression-graphs/{id}` | Detail; `?include_edges=true` |
| POST | `/exercise-progression-graphs` | Graph anlegen |
| PUT | `/exercise-progression-graphs/{id}` | Metadaten |
| DELETE | `/exercise-progression-graphs/{id}` | Graph + Kanten |
| GET | `/exercise-progression-graphs/{id}/edges` | Kanten; Query optional `from_exercise_id`, `to_exercise_id` |
| POST | `/exercise-progression-graphs/{id}/edges` | Einzelkante; Duplikat/Constraint → **409** |
| POST | `/exercise-progression-graphs/{id}/edges/sequence` | **Bulk:** `{ steps: [{ exercise_id, variant_id? }, …], segment_notes?: [...] }` — nur **`next_exercise`**, Transaktion alle oder keine Zeile |
| PUT | `/exercise-progression-graphs/{id}/edges/{edge_id}` | z. B. **`notes`** |
| DELETE | `/exercise-progression-graphs/{id}/edges/{edge_id}` | eine Kante |
| POST | `/exercise-progression-graphs/{id}/edges/delete-batch` | `{ edge_ids: [...] }` — z. B. gesamte sichtbare „Reihe“ löschen |

### 3.4 Frontend (Stand Code)

- **`ExercisesListPage`:** Tabs **Liste** · **Progressionsgraphen** → **`ExerciseProgressionGraphPanel`**.
- **`ExerciseFormPage`** (nur Edit): eingeklappter Block **Progressionsgraph** mit Kontext „diese Übung“ + Filter „nur betroffene Kanten“.
- Panel‑Funktionen: **Sequenz‑Editor** (mehrere Schritte → ein Bulk‑Speichern), zusammengefasste **Reihen‑Lesart** für `next_exercise`, eigene Liste für **Schwestern**, Einzelkantenbereich, Tab **Alle Kanten (Tabelle)**.
- API‑Client: `frontend/src/utils/api.js` (`createExerciseProgressionSequence`, `deleteExerciseProgressionEdgesBatch`, …).

---

## 4. Zwischenstand für Produkt / Trainingsplanung (bewusste Grenzen)

**Freigabe:** Der beschriebene Stand unterstützt **Rahmen‑Bibliothek mit vollem Ablauf pro Slot** (wie Planung) und **Kopie in die Gruppenplanung**; der **Progressionsgraph** bleibt **unterstützend** (**CURR‑013**). Offen: Kalender‑UI‑Flow, Bulk‑Instanziierung, erweiterte Lineage/Feedback (**Konzept Schritt E**).

**Was gut nutzbar ist**

- Lineare **Reihen** mehrerer Übungen (bzw. Varianten‑Knoten) über **Sequenz‑API** bzw. Sequenz‑UI.
- **Nachfolger‑Lesart** als zusammenhängende Kette in der Übersicht.
- **Schwester‑Kanten** als eigene Liste (Alternative gleicher „Stufe“ zwischen zwei Knoten).
- **Einzelkanten** für Sonderfälle und Verzweigungen.

**Was noch nicht „ein Knopf“ ist (bekannt)**  

- **Parallele gleichwertige Alternativen** („Paket B bestehend aus mehreren Übungen als echte Alternative zu Paket A“) sind **nicht** als erste‑Klass‑UX modelliert: mehrere Nachfolger aus einem Knoten sind technisch möglich (mehrere `next_exercise`‑Kanten), aber **keine** dedizierte Gruppe „Alternativ‑Set“. Pflege kann mehrzeilig und koplastisch wirken.  
- **Visualisierung echter Bäume** (Join‑Points, mehrere ausgehende Pfeile in einem Bild) ist nur eingeschränkt über Reihen‑Zusammenfassung + Tabelle abbildbar.

**Nächste sinnvolle Ausbaustufen** (Backlog Graph‑UX, nicht Blocker Planung)

- Semantik **`alternative_group_id`** oder **Hyperkanten** (ein UX‑Schritt legt mehrere Kanten mit gemeinsamer Gruppe an).
- Komfort beim Pflegen **symmetrischer Schwestern** (ein Klick für zwei Richtungen / Dedupe).
- Karten-/Baum‑Layout statt nur Zeilen.

Details weiterhin Diskussionsgrundlage in `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` §6 (Kantentypen).

---

## 5. Changelog (Dokument)

| Datum | Änderung |
|-------|----------|
| 2026-05-05 | **037 / API:** Nur Bibliothek + **Blueprint** pro Slot über `training_units` + Sektionen/Items; `training_framework_slot_exercises` entfernt; `POST …/training-units/from-framework-slot`; Planungsliste ohne Blueprints. **036** dokumentiert am Kopf (Kontext, M:N, kein plan_mode/group_id). **§2** vollständig ersetzt. |
| 2026-05-05 | **CURR‑002 (2):** §2 Rahmenprogramm — Entscheid **eine Tabelle + `plan_mode`**, DDL‑Skizze, REST‑Überblick; Migration **035**; `training_plan_templates.visibility`. *(Historisch — Modus-Spalten durch **036** ersetzt.)* |
| 2026-04-30 | **Zwischen-Doku:** §3 auf Migrationen 032–034 + API **sequence/delete-batch** + Frontend erweitert; **§4** Produktfreigabe vs. Lücken (parallele Alternativen); Changelog §5. |
| 2026-04-30 | §3: erste Fassung Migration 032 + REST‑Basis (CURR‑002 (1)). |
| 2026-04-28 | Erstanlage Stub mit Checkliste. |
