# Fähigkeitsmatrix / Reifegradmodell – Specification

**Version:** 1.0
**Datum:** 2026-04-24
**Status:** DRAFT
**Autor:** Claude Code
**Basis:** shinkan_anforderungsdokument_entwurf.md §8 + DOMAIN_MODEL.md

---

## 1. Konzept

### 1.1 Was ist die Fähigkeitsmatrix?

Die Fähigkeitsmatrix verbindet **globale Fähigkeiten** (Skills) mit **kontextspezifischen
Reifegradmodellen**. Sie beantwortet pro Kontext die Frage:

> „Was muss ein Schüler auf Stufe X dieser Fähigkeit können, und welche Übungen trainieren ihn von Stufe A auf Stufe B?"

**Drei Ebenen:**

```
1. Globale Fähigkeit (skill)
   "Distanzgefühl" – gilt überall, für alle Stile und Zielgruppen

2. Reifegradmodell (maturity_model)
   "Karate / Shotokan / Breitensport" – definiert WIE VIELE Stufen es gibt
   und wie die Stufen heißen

3. Modell-Fähigkeitsstufe (model_skill_level)
   "Stufe 3 von Distanzgefühl im Shotokan-Breitensport-Modell" –
   beschreibt KONKRET was auf Stufe 3 erwartet wird
```

### 1.2 Warum kontextabhängig?

Dieselbe Fähigkeit (z.B. „Distanzgefühl") hat in verschiedenen Kontexten
unterschiedliche Bedeutungen:

| Kontext | Stufe 3 Bedeutung |
|---------|-------------------|
| Karate / Shotokan / Breitensport | Distanzkontrolle in einfachen Partnerübungen |
| Karate / Shotokan / Leistungssport | Stabile Distanz im freien Kumite |
| Selbstverteidigung / Erwachsene | Sicherheitsabstand in Alltagssituationen einschätzen |

Ein Modell hat **variable Stufenanzahl** (3-7, nicht fest 5):

```
Shotokan Breitensport → 5 Stufen (Einsteiger bis Experte)
Kinder-Karate        → 4 Stufen (Kinder-gerecht vereinfacht)
Leistungssport       → 7 Stufen (feiner granuliert)
```

### 1.3 Abgrenzung zu exercise_skills

```
exercise_skills:     Übung trainiert Fähigkeit mit Intensität und
                     suggested required/target Stufe
                     → kontextunabhängig (gilt für alle Modelle)

model_skill_levels:  Für dieses Modell bedeutet "Stufe 3" von Fähigkeit X:
                     "[konkrete Beschreibung]"
                     → kontextspezifisch
```

---

## 2. Datenmodell

### 2.1 Entity-Übersicht

```
focus_areas (global, existiert)
    └─ style_directions (existiert, früher training_styles)
        └─ target_groups (M:N, existiert)

maturity_models (NEU)
    ├─ focus_area_id FK (optional, NULL = gilt für alle Fokusbereiche)
    ├─ style_direction_id FK (optional)
    ├─ target_group_id FK (optional)
    └─ level_count (INT) – wie viele Stufen hat das Modell?

model_levels (NEU) – Stufen-Definitionen
    ├─ maturity_model_id FK
    ├─ level_number (1 bis level_count)
    ├─ name (z.B. "Einsteiger", "Grundlagen", ...)
    └─ description

model_skill_levels (NEU) – Was bedeutet Stufe X für Fähigkeit Y im Modell Z?
    ├─ maturity_model_id FK
    ├─ skill_id FK
    ├─ level_number (muss in maturity_model.level_count liegen)
    ├─ description (Pflicht-Text: was soll der Lernende können?)
    ├─ observable_criteria (Beobachtungskriterien für Trainer)
    └─ example_exercises JSONB (Empfohlene Übungs-Typen, keine FK)
```

### 2.2 Vollständige Migration (019_maturity_models.sql)

```sql
-- Migration 019: Fähigkeitsmatrix / Reifegradmodelle
-- Autor: Claude Code
-- Datum: 2026-04-24

DO $$
BEGIN

-- ============================================================================
-- MATURITY MODELS (Reifegradmodelle)
-- ============================================================================

CREATE TABLE IF NOT EXISTS maturity_models (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Kontext-Bindung (alle optional – NULL bedeutet "gilt allgemein")
    -- Je mehr gesetzt, desto spezifischer das Modell
    focus_area_id       INT REFERENCES focus_areas(id)       ON DELETE RESTRICT,
    style_direction_id  INT REFERENCES style_directions(id)  ON DELETE RESTRICT,
    target_group_id     INT REFERENCES target_groups(id)     ON DELETE RESTRICT,

    -- Stufenanzahl (flexibel, nicht fest auf 5)
    level_count INT NOT NULL DEFAULT 5 CHECK (level_count BETWEEN 3 AND 10),

    -- Sichtbarkeit & Freigabe
    -- 'draft': in Bearbeitung | 'active': in Nutzung | 'archived': nicht mehr aktiv
    status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'active', 'archived')),

    -- Versionierung
    version VARCHAR(20) DEFAULT '1.0',

    -- Ownership
    created_by  INT REFERENCES profiles(id) ON DELETE SET NULL,
    club_id     INT REFERENCES clubs(id)    ON DELETE SET NULL,

    -- Import-Tracking (für Semantic MediaWiki Import)
    import_source VARCHAR(50),
    import_id     VARCHAR(200),

    -- Timestamps
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),

    -- Constraint: Name + Kontext eindeutig
    UNIQUE(name, focus_area_id, style_direction_id, target_group_id)
);

-- ============================================================================
-- MODEL LEVELS (Stufendefinitionen pro Modell)
-- ============================================================================

CREATE TABLE IF NOT EXISTS model_levels (
    id SERIAL PRIMARY KEY,
    maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE,
    level_number      INT NOT NULL CHECK (level_number >= 1),
    name              VARCHAR(100) NOT NULL,  -- z.B. "Einsteiger", "Grundlagen", "Aufbau"
    description       TEXT,                  -- Was zeichnet diese Stufe generell aus?
    sort_order        INT NOT NULL,

    created_at TIMESTAMP DEFAULT NOW(),

    UNIQUE(maturity_model_id, level_number)
);

-- ============================================================================
-- MODEL SKILL LEVELS
-- Was bedeutet "Stufe X der Fähigkeit Y im Modell Z"?
-- ============================================================================

CREATE TABLE IF NOT EXISTS model_skill_levels (
    id SERIAL PRIMARY KEY,
    maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE,
    skill_id          INT NOT NULL REFERENCES skills(id)          ON DELETE RESTRICT,
    level_number      INT NOT NULL CHECK (level_number >= 1),

    -- Was wird auf dieser Stufe erwartet? (Pflichtfeld)
    description TEXT NOT NULL,

    -- Konkrete Beobachtungskriterien für Trainer
    -- z.B. "Kann Distanz in ruhigen Partnerübungen halten (3/3 Versuchen)"
    observable_criteria TEXT,

    -- Empfohlene Übungs-Typen (keine FK, nur Hinweise)
    -- z.B. {"types": ["Grundübung", "Partnerübung"], "max_duration_min": 15}
    example_exercise_hints JSONB,

    -- KI-generiert?
    ai_generated BOOLEAN DEFAULT false,

    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),

    UNIQUE(maturity_model_id, skill_id, level_number),

    -- level_number muss im Bereich des Modells liegen
    -- (wird per Trigger oder Backend-Validierung geprüft)
    CONSTRAINT ck_level_positive CHECK (level_number > 0)
);

-- ============================================================================
-- SKILL CATALOG: Felder für Matrix-Nutzung (falls noch nicht vorhanden)
-- ============================================================================

-- Fähigkeiten können einem bestimmten Fokusbereich "zugehören" (primär)
-- bleiben aber global nutzbar (secondary assignments über M:N)
ALTER TABLE skills
ADD COLUMN IF NOT EXISTS primary_focus_area_id INT REFERENCES focus_areas(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS is_cross_domain BOOLEAN DEFAULT false;  -- gilt für alle Fokusbereiche

-- ============================================================================
-- INDEXES
-- ============================================================================

CREATE INDEX IF NOT EXISTS idx_maturity_models_focus    ON maturity_models(focus_area_id);
CREATE INDEX IF NOT EXISTS idx_maturity_models_style    ON maturity_models(style_direction_id);
CREATE INDEX IF NOT EXISTS idx_maturity_models_target   ON maturity_models(target_group_id);
CREATE INDEX IF NOT EXISTS idx_maturity_models_status   ON maturity_models(status);

CREATE INDEX IF NOT EXISTS idx_model_levels_model       ON model_levels(maturity_model_id);

CREATE INDEX IF NOT EXISTS idx_model_skill_levels_model ON model_skill_levels(maturity_model_id);
CREATE INDEX IF NOT EXISTS idx_model_skill_levels_skill ON model_skill_levels(skill_id);
CREATE INDEX IF NOT EXISTS idx_model_skill_levels_combo ON model_skill_levels(maturity_model_id, skill_id);

-- ============================================================================
-- TRIGGERS
-- ============================================================================

DROP TRIGGER IF EXISTS maturity_models_update ON maturity_models;
CREATE TRIGGER maturity_models_update
BEFORE UPDATE ON maturity_models
FOR EACH ROW EXECUTE FUNCTION update_timestamp();

DROP TRIGGER IF EXISTS model_skill_levels_update ON model_skill_levels;
CREATE TRIGGER model_skill_levels_update
BEFORE UPDATE ON model_skill_levels
FOR EACH ROW EXECUTE FUNCTION update_timestamp();

RAISE NOTICE 'Migration 019 completed successfully (Maturity Models / Fähigkeitsmatrix)';

END $$;
```

---

## 3. Beispiel-Daten

### 3.1 Beispiel-Modell: Karate Shotokan Breitensport (5 Stufen)

```sql
INSERT INTO maturity_models (name, focus_area_id, style_direction_id, target_group_id, level_count, status)
VALUES ('Karate Shotokan Breitensport', 1, 1, 1, 5, 'active');

-- Stufennamen
INSERT INTO model_levels (maturity_model_id, level_number, name, description, sort_order) VALUES
(1, 1, 'Einsteiger',      'Erste Begegnung – keine Vorkenntnisse erforderlich', 1),
(1, 2, 'Grundlagen',      'Grundprinzipien bekannt, in ruhigen Situationen anwendbar', 2),
(1, 3, 'Aufbau',          'Semi-intuitiver Einsatz, mit gelegentlicher Korrektur', 3),
(1, 4, 'Fortgeschritten', 'Intuitiver Einsatz auch unter Druck', 4),
(1, 5, 'Experte',         'Vollständig automatisiert, stabile Leistung auf Spitzenniveau', 5);

-- Fähigkeitsstufen: Distanzgefühl im Shotokan-Breitensport-Modell
INSERT INTO model_skill_levels (maturity_model_id, skill_id, level_number, description, observable_criteria)
VALUES
(1, 10, 1, 'Versteht das Konzept "Distanz" und kann erklären warum es wichtig ist.',
    'Kann auf Frage benennen, was Maai bedeutet.'),
(1, 10, 2, 'Hält in langsamen, geführten Partnerübungen die Angriffsdistanz.',
    'In 3 von 3 langsamen Wiederholungen korrekte Distanz eingenommen.'),
(1, 10, 3, 'Kontrolliert Distanz in mittlerer Geschwindigkeit bei Standardtechniken.',
    'Trainer bewertet Distanzkontrolle mit "gut" in Kihon-Ippon-Kumite.'),
(1, 10, 4, 'Stabiler Einsatz im Jiyu-Ippon-Kumite, selten vom Partner überrascht.',
    'In freiem Jiyu-Kumite hält Schüler Linie in >70% der Aktionen.'),
(1, 10, 5, 'Feingefühl für minimale Distanzveränderungen, taktischer Einsatz im Shiai.',
    'Wettkampferfolge durch bewusstes Distanzmanagement nachweisbar.');
```

---

## 4. API-Endpoints

### 4.1 Übersicht

| Method | Endpoint | Beschreibung |
|--------|----------|--------------|
| GET | `/maturity-models` | Liste aller Modelle (mit Filter) |
| GET | `/maturity-models/{id}` | Modell-Detail mit Stufen + Skills |
| POST | `/maturity-models` | Neues Modell erstellen (Admin) |
| PUT | `/maturity-models/{id}` | Modell bearbeiten (Admin) |
| DELETE | `/maturity-models/{id}` | Modell löschen (Admin) |
| GET | `/maturity-models/{id}/skills` | Alle Fähigkeiten + Stufen dieses Modells |
| PUT | `/maturity-models/{id}/skills/{skill_id}/levels` | Stufen-Beschreibungen setzen (Admin) |
| GET | `/maturity-models/resolve` | Bestes Modell für Kontext finden |

### 4.2 `GET /maturity-models/resolve`

Findet das spezifischste Modell für einen gegebenen Kontext.
Wird von der KI und vom Frontend verwendet.

**Query Parameters:**
- `focus_area_id` (int, optional)
- `style_direction_id` (int, optional)
- `target_group_id` (int, optional)

**Response:**
```json
{
  "model": {
    "id": 1,
    "name": "Karate Shotokan Breitensport",
    "level_count": 5,
    "match_specificity": "full"
  },
  "fallback_used": false
}
```

**Auflösungslogik (Most Specific First):**
1. Alle drei passen (focus + style + target) → `match_specificity: "full"`
2. focus + style passen → `match_specificity: "style"`
3. Nur focus passt → `match_specificity: "focus"`
4. Allgemeines Modell (alle FK = NULL) → `match_specificity: "generic"`
5. Kein Modell vorhanden → `match_specificity: null`, 404

---

## 5. Verbindung zu Übungen

### 5.1 Wie exercise_skills das Modell referenziert

`exercise_skills` speichert `required_level` und `target_level` als **Stufen-Namen**
(einsteiger/grundlagen/aufbau/fortgeschritten/experte), die modellunabhängig sind.

Das Frontend kann bei Bedarf die konkrete Modell-Beschreibung nachladen:

```
exercise_skills.required_level = "grundlagen"
→ GET /maturity-models/resolve?focus_area_id=1
→ Model: Karate Shotokan Breitensport (level_count=5)
→ model_levels: level_number=2, name="Grundlagen"
→ model_skill_levels für skill_id=10: "Hält in ruhigen Partnerübungen Distanz"
```

### 5.2 Darstellung in der Übungsdetail-Ansicht

```
Fähigkeiten
─────────────────────────────────────────────
★ Distanzgefühl                    (primär)
  Voraussetzung: Grundlagen  →  Ziel: Aufbau
  Intensität: Hoch
  ────────────────────────────────
  Grundlagen: "Hält in ruhigen Partnerübungen
               Distanz"
  Aufbau: "Kontrolle in mittlerer
            Geschwindigkeit"
  [Kontext: Shotokan Breitensport ▼]
─────────────────────────────────────────────
◦ Reaktionsschnelligkeit           (sekundär)
  Voraussetzung: Einsteiger  →  Ziel: Grundlagen
  Intensität: Mittel
```

**Kontext-Dropdown:** Trainer kann Modell wechseln um kontextspezifische Beschreibungen zu sehen.

---

## 6. KI-Unterstützung für Modell-Pflege

Die KI kann `model_skill_levels`-Beschreibungen vorschlagen.

**Prompt-Placeholder:**
- `{{skill_name}}` – Name der Fähigkeit
- `{{model_name}}` – Name des Reifegradmodells
- `{{level_count}}` – Anzahl der Stufen
- `{{level_names}}` – Namen der Stufen (z.B. "Einsteiger, Grundlagen, Aufbau...")
- `{{focus_area}}` – Fokusbereich
- `{{target_group}}` – Zielgruppe

**Endpoint:** `POST /maturity-models/{id}/ai/suggest-skill-levels`

```json
// Request
{
  "skill_id": 10,
  "regenerate_existing": false
}

// Response
{
  "skill": {"id": 10, "name": "Distanzgefühl"},
  "suggestions": [
    {
      "level_number": 1,
      "level_name": "Einsteiger",
      "description": "...",
      "observable_criteria": "...",
      "ai_generated": true
    },
    ...
  ]
}
```

---

## 7. Semantic MediaWiki Import

Die bestehende Fähigkeitsmatrix im SMW enthält bereits Stufen-Beschreibungen.

**Erweiterung der MEDIAWIKI_IMPORT_SPEC.md:**

| Wiki-Feld | Ziel-Tabelle | Transformation |
|-----------|-------------|----------------|
| SMW Property: `Stufe 1 Beschreibung` | `model_skill_levels.description` (level=1) | Direkt |
| SMW Property: `Stufe N Beschreibung` | `model_skill_levels.description` (level=N) | Direkt |
| SMW Property: `Modell` | `maturity_models.name` | Lookup/Create |
| SMW Property: `Fokusbereich` | `maturity_models.focus_area_id` | Name → ID |

**Import-Endpoint:** `POST /import/mediawiki/execute` mit `import_type: "maturity_model"`

---

## 8. Admin-Workflow

### 8.1 Modell anlegen (Admin)

```
1. Admin wählt: Fokusbereich + Stil + Zielgruppe
2. Admin legt Stufenanzahl + Stufennamen fest
3. Admin weist Fähigkeiten dem Modell zu (Pflicht-/Optional-Skills)
4. Für jede Fähigkeit × Stufe: Beschreibung eingeben
   → Optional: KI-Vorschlag generieren
5. Modell auf "active" stellen
```

### 8.2 SMW-Migration-Workflow

```
1. SMW-Import: Bestehende Modelle und Beschreibungen importieren
2. Review: Trainer prüft importierte Beschreibungen
3. KI-Ergänzung: Für fehlende Stufen KI-Vorschläge generieren
4. Freigabe: status → 'active'
```

---

**Version:** 1.0
**Datum:** 2026-04-24
**Status:** DRAFT
**Nächste Schritte:**
- SMW-Struktur analysieren (User legt Daten ins Repo)
- Migration 019 implementieren
- Admin-UI für Modellpflege spezifizieren (UI_COMPONENTS_SPEC.md ergänzen)
