diff --git a/backend/migrations/092_catalog_prompt_slots.sql b/backend/migrations/092_catalog_prompt_slots.sql index db787fe..5178054 100644 --- a/backend/migrations/092_catalog_prompt_slots.sql +++ b/backend/migrations/092_catalog_prompt_slots.sql @@ -1,4 +1,6 @@ --- Migration 092: Katalog-Prompt-Slots (H2) — Slot-Typ-Vokabular + Werte pro Stammdaten-Zeile +-- Migration 092: Katalog-Prompt-Slots (H2) — Slot-Typ-Vokabular + leere Wertetabelle +-- Keine Inhalts-Seeds: Stammdaten (Primärfokus etc.) sind mandantenspezifisch. +-- Slot-Inhalte: Admin-UI oder catalog_slot_fallbacks.py (Namens-Match zur Laufzeit). CREATE TABLE IF NOT EXISTS catalog_prompt_slot_types ( slot_key VARCHAR(64) PRIMARY KEY, @@ -82,95 +84,3 @@ VALUES true ) ON CONFLICT (slot_key) DO NOTHING; - --- Seed aus H1-Registry (Name-Match auf Stammdaten) - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'focus_area', fa.id, 'description', - 'Planung zielt auf Prävention, Deeskalation, Grenzen und sichere Übungsformen — nicht auf Wettkampf-Perfektion oder Technik-Show.' -FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'focus_area', fa.id, 'hints_on_path_qa', - 'Gute Pfade bauen Sicherheit, Kommunikation und Alternativen auf; „Lücken“ sind fehlende Deeskalations- oder Rollenspiel-Stufen, nicht fehlende Kick-Varianten.' -FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'focus_area', fa.id, 'anti_patterns', - 'Nicht nach Kumite-Tiefe, Explosivität oder Wettkampf-Belastung bewerten.' -FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'training_type', tt.id, 'description', - 'Partizipation, Verständlichkeit, Freude am Bewegen; weniger maximale Spezialisierung.' -FROM training_types tt WHERE tt.name ILIKE 'Breitensport' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'training_type', tt.id, 'hints_on_path_qa', - 'Hohe OK-Rate bei moderatem Schwierigkeitsanstieg; „Perfektion“-Stufen nur optional, nicht als Pflicht-Lücke.' -FROM training_types tt WHERE tt.name ILIKE 'Breitensport' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'training_type', tt.id, 'rematch_guard', - 'Keine leeren Slots erzwingen, nur um eine Leistungs-Perfektionsstufe zu füllen.' -FROM training_types tt WHERE tt.name ILIKE 'Breitensport' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'target_group', tg.id, 'description', - 'Kinder: kurze Einheiten, spielerische Einstiege, Sicherheit und altersgerechte Komplexität.' -FROM target_groups tg WHERE tg.name ILIKE 'Kinder' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'target_group', tg.id, 'hints_on_path_qa', - 'Didaktik ohne Überforderung; klare Regeln und Sicherheit vor Perfektion; Lücken bei Spiel-/Rollenelementen wichtiger als Wettkampftiefe.' -FROM target_groups tg WHERE tg.name ILIKE 'Kinder' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'target_group', tg.id, 'anti_patterns', - 'Keine Erwachsenen-Wettkampf-Perfektion als QS-Maßstab.' -FROM target_groups tg WHERE tg.name ILIKE 'Kinder' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'target_group', tg.id, 'description', - 'Leistungsgruppe: höhere Anspruchskurven und Spezialisierung sind fachlich passend.' -FROM target_groups tg WHERE tg.name ILIKE 'Leistungssportler' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'target_group', tg.id, 'hints_on_path_qa', - 'Höhere Anspruchskurven, Belastungs- und Kombinationsprogressionen sind relevant; Lücken in Spezialisierung können echte Hinweise sein.' -FROM target_groups tg WHERE tg.name ILIKE 'Leistungssportler' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'style_direction', sd.id, 'description', - 'Shotokan-Linie: klare Kihon-Struktur, Hüft- und Standarbeit als wiederkehrende Qualitätsanker.' -FROM style_directions sd WHERE sd.name ILIKE 'Shotokan' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'style_direction', sd.id, 'hints_on_progression', - 'Nuancen in Stellung und Hüfttechnik, kein neuer Planungstyp.' -FROM style_directions sd WHERE sd.name ILIKE 'Shotokan' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'training_type', tt.id, 'description', - 'Wettkampforientiertes Training mit höherer Anspruchskurve und belastungsnahen Phasen.' -FROM training_types tt WHERE tt.name ILIKE 'Wettkampf' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'training_type', tt.id, 'hints_on_path_qa', - 'Spezialisierung, Kombination und Belastung unter Druck sind relevant; Lücken in Anwendungs- oder Perfektionsphasen können echte Hinweise sein.' -FROM training_types tt WHERE tt.name ILIKE 'Wettkampf' -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); diff --git a/backend/migrations/094_catalog_prompt_slots_full_seed.sql b/backend/migrations/094_catalog_prompt_slots_full_seed.sql index ad675cd..e9d0189 100644 --- a/backend/migrations/094_catalog_prompt_slots_full_seed.sql +++ b/backend/migrations/094_catalog_prompt_slots_full_seed.sql @@ -1,167 +1,11 @@ --- Migration 094: Vollständige Befüllung catalog_prompt_slots (H1-Inhalte + Defaults für alle Stammdaten) +-- Migration 094: bewusst ohne Daten-Seed (ersetzt frühere Voll-Befüllung) +-- +-- Katalog-Stammdaten (Primärfokus, Trainingsstile, …) sind pro Umgebung unterschiedlich. +-- Migrationen dürfen keine Dev-Standardtexte an Prod-Kataloge hängen. +-- +-- Slot-Inhalte stattdessen: +-- • Admin-UI (catalog_prompt_slots pro Eintrag) +-- • Laufzeit-Fallback catalog_slot_fallbacks.py (Namens-Match, kein DB-Schreiben) +-- • optional: backend/scripts/seed_catalog_prompt_slots_dev.py (nur lokale Dev-DB) -CREATE TEMP TABLE IF NOT EXISTS _catalog_slot_seed ( - catalog_kind VARCHAR(32) NOT NULL, - name_pattern TEXT NOT NULL, - slot_key VARCHAR(64) NOT NULL, - content TEXT NOT NULL -); - -TRUNCATE _catalog_slot_seed; - --- Primärfokus Karate (häufigster Technik-Pfad) -INSERT INTO _catalog_slot_seed (catalog_kind, name_pattern, slot_key, content) VALUES -('focus_area', 'Karate', 'description', - 'Technik-Curriculum im Karate-Kontext: aufeinander aufbauende Kihon-Progression mit klaren Qualitätsankern (Stand, Hüfte, Kime).'), -('focus_area', 'Karate', 'hints_on_progression', - 'Typische Phasen: Einstieg → Grundlagen → Koordination/Kraft → Anwendung → optional Vertiefung; Grundlagen vor Perfektion.'), -('focus_area', 'Karate', 'hints_on_exercise', - 'Kihon und Partnerübungen mit Technikbezug; reine Kraft-/Ausdauer-Inseln nur mit klarer Begründung.'), -('focus_area', 'Karate', 'hints_on_path_qa', - 'Kohärente Progression Grundlagen → Anwendung → Vertiefung; Übergänge ohne Sprünge; themenfremde Kraft-/Ausdauer-Inseln abwerten.'), -('focus_area', 'Karate', 'anti_patterns', - 'Keine pauschale Perfektions-Stufe verlangen, wenn der Trainingsstil Breitensport ist.'); - --- Selbstverteidigung -INSERT INTO _catalog_slot_seed VALUES -('focus_area', 'Selbstverteidigung', 'description', - 'Praktische Selbstverteidigung: realistische Szenarien, Sicherheit und anwendungsnahe Progression — nicht Show-Technik oder Wettkampf-Kata.'), -('focus_area', 'Selbstverteidigung', 'hints_on_progression', - 'Von Wahrnehmung und Distanz zu einfachen Abwehrmustern und kontrollierter Anwendung.'), -('focus_area', 'Selbstverteidigung', 'hints_on_exercise', - 'Partnerübungen mit klaren Sicherheitsregeln; Szenario-Bezug wichtiger als Stil-Show.'), -('focus_area', 'Selbstverteidigung', 'hints_on_path_qa', - 'Lücken bei Szenario- oder Sicherheitsstufen sind relevant; fehlende Kick-Varianten oder Wettkampftiefe sind kein Mangel.'), -('focus_area', 'Selbstverteidigung', 'anti_patterns', - 'Keine Wettkampf- oder Kata-Perfektion als QS-Maßstab.'); - --- Gewaltschutz (ergänzt 092) -INSERT INTO _catalog_slot_seed VALUES -('focus_area', 'Gewaltschutz', 'hints_on_progression', - 'Phasen: Wahrnehmung → Grenzen → Deeskalation → sichere Übungsformen; keine Kumite-Perfektionsstufen erzwingen.'), -('focus_area', 'Gewaltschutz', 'hints_on_exercise', - 'Übungen mit Rollen, Kommunikation, Ausweichen; keine rein technischen Kick-Fokus-Inseln ohne Bezug.'); - --- Fitness (falls vorhanden) -INSERT INTO _catalog_slot_seed VALUES -('focus_area', 'Fitness', 'description', - 'Fitness- und Konditionsorientierung mit sicherer Belastungssteuerung; Technikbezug nur wo fachlich sinnvoll.'), -('focus_area', 'Fitness', 'hints_on_progression', - 'Progression von niedriger zu moderater Belastung; klare Pausen und Technikhygiene.'), -('focus_area', 'Fitness', 'hints_on_path_qa', - 'Keine Wettkampf-Spezialisierung als Pflicht-Kriterium; Belastungssteigerung ohne Technikbezug abwerten.'), -('focus_area', 'Fitness', 'anti_patterns', - 'Keine Kumite-Perfektion oder Wettkampf-Kombinationen als QS-Maßstab verlangen.'); - --- Trainingsstile (global) -INSERT INTO _catalog_slot_seed VALUES -('training_type', 'Breitensport', 'hints_on_progression', - 'Moderater Schwierigkeitsanstieg; Perfektionsphasen optional.'), -('training_type', 'Breitensport', 'anti_patterns', - 'Keine Leistungssport-Perfektion als Pflicht-Lücke.'), -('training_type', 'Leistungssport', 'description', - 'Leistungsorientiertes Training mit höherer Anspruchskurve und Spezialisierung.'), -('training_type', 'Leistungssport', 'hints_on_progression', - 'Belastungs- und Kombinationsprogressionen sind erwünscht.'), -('training_type', 'Leistungssport', 'hints_on_path_qa', - 'Höhere Anspruchskurven sind passend; Lücken in Spezialisierung können echte Hinweise sein.'), -('training_type', 'Wettkampf', 'hints_on_progression', - 'Anwendungs- und Druckphasen zeitig einplanen.'); - --- Zielgruppen -INSERT INTO _catalog_slot_seed VALUES -('target_group', 'Breitensportler', 'description', - 'Breitensport: Partizipation und Verständlichkeit vor maximaler Spezialisierung.'), -('target_group', 'Breitensportler', 'hints_on_path_qa', - 'Moderate Progression; Perfektions-Lücken sind selten echte Mängel.'), -('target_group', 'Breitensportler', 'anti_patterns', - 'Keine Leistungssport-Perfektion als Pflicht-Kriterium.'), -('target_group', 'Kinder', 'hints_on_progression', - 'Spielerische Einstiege; kurze Abschnitte; Sicherheit vor Perfektion.'), -('target_group', 'Leistungssportler', 'hints_on_progression', - 'Anspruchskurve und Spezialisierung dürfen steiler sein.'); - --- Stilrichtungen (generisch + Shotokan-Details via 092) -INSERT INTO _catalog_slot_seed VALUES -('style_direction', 'Goju-Ryu', 'hints_on_progression', - 'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'), -('style_direction', 'Wado-Ryu', 'hints_on_progression', - 'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'), -('style_direction', 'Shito-Ryu', 'hints_on_progression', - 'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'), -('style_direction', 'Kyokushin', 'hints_on_progression', - 'Stil-Nuancen (Stand, Belastung, Kime) einbeziehen — kein Stilwechsel erzwingen.'); - --- Fokusbereiche: aus Seed-Tabelle -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT s.catalog_kind, fa.id, s.slot_key, s.content -FROM _catalog_slot_seed s -JOIN focus_areas fa ON fa.name ILIKE s.name_pattern -WHERE s.catalog_kind = 'focus_area' -ON CONFLICT (catalog_kind, catalog_id, slot_key) -DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT s.catalog_kind, tt.id, s.slot_key, s.content -FROM _catalog_slot_seed s -JOIN training_types tt ON tt.name ILIKE s.name_pattern -WHERE s.catalog_kind = 'training_type' -ON CONFLICT (catalog_kind, catalog_id, slot_key) -DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT s.catalog_kind, tg.id, s.slot_key, s.content -FROM _catalog_slot_seed s -JOIN target_groups tg ON tg.name ILIKE s.name_pattern -WHERE s.catalog_kind = 'target_group' -ON CONFLICT (catalog_kind, catalog_id, slot_key) -DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT s.catalog_kind, sd.id, s.slot_key, s.content -FROM _catalog_slot_seed s -JOIN style_directions sd ON sd.name ILIKE s.name_pattern -WHERE s.catalog_kind = 'style_direction' -ON CONFLICT (catalog_kind, catalog_id, slot_key) -DO UPDATE SET content = EXCLUDED.content, updated_at = NOW(); - --- Default-Technik-Pack für Fokusbereiche ohne hints_on_path_qa (außer Gewaltschutz/Fitness) -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'focus_area', fa.id, 'hints_on_path_qa', - 'Kohärente Progression zum Anfrage-Thema; Lücken sind fehlende Zwischenstufen im Lernpfad, nicht fehlende Nebenthemen.' -FROM focus_areas fa -WHERE fa.name NOT ILIKE 'Gewaltschutz' - AND fa.name NOT ILIKE 'Fitness' - AND NOT EXISTS ( - SELECT 1 FROM catalog_prompt_slots cps - WHERE cps.catalog_kind = 'focus_area' AND cps.catalog_id = fa.id AND cps.slot_key = 'hints_on_path_qa' - AND TRIM(cps.content) <> '' - ) -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING; - -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'focus_area', fa.id, 'hints_on_progression', - 'Grundlagen vor Anwendung; moderate Sprünge zwischen Stufen vermeiden.' -FROM focus_areas fa -WHERE fa.name NOT ILIKE 'Gewaltschutz' - AND fa.name NOT ILIKE 'Fitness' - AND NOT EXISTS ( - SELECT 1 FROM catalog_prompt_slots cps - WHERE cps.catalog_kind = 'focus_area' AND cps.catalog_id = fa.id AND cps.slot_key = 'hints_on_progression' - AND TRIM(cps.content) <> '' - ) -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING; - --- Stilrichtungen ohne Eintrag: generischer Progressions-Hinweis -INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content) -SELECT 'style_direction', sd.id, 'hints_on_progression', - 'Stil-spezifische Nuancen (Stand, Hüfte, Rhythmus) einbeziehen — ohne Stilwechsel zu erzwingen.' -FROM style_directions sd -WHERE NOT EXISTS ( - SELECT 1 FROM catalog_prompt_slots cps - WHERE cps.catalog_kind = 'style_direction' AND cps.catalog_id = sd.id AND cps.slot_key = 'hints_on_progression' - AND TRIM(cps.content) <> '' - ) -ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING; - -DROP TABLE IF EXISTS _catalog_slot_seed; +SELECT 1; diff --git a/backend/migrations/095_catalog_prompt_slots_clear_migration_seeds.sql b/backend/migrations/095_catalog_prompt_slots_clear_migration_seeds.sql new file mode 100644 index 0000000..3e84e26 --- /dev/null +++ b/backend/migrations/095_catalog_prompt_slots_clear_migration_seeds.sql @@ -0,0 +1,7 @@ +-- Migration 095: Entfernt per 092/094 (alte Version) eingespielte Standard-Slot-Texte +-- +-- Betrifft Dev-Umgebungen, die die frühen Seed-Migrationen bereits erhalten haben. +-- Prod mit eigener Katalogstruktur: Tabelle bleibt leer bis Admin-Inhalte gepflegt werden. +-- Manuell in der Admin-UI gesetzte Texte nach 095 bitte erneut prüfen (waren ggf. identisch mit Seeds). + +DELETE FROM catalog_prompt_slots; diff --git a/docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md b/docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md index 183dfe4..19e29f6 100644 --- a/docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md +++ b/docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md @@ -115,6 +115,8 @@ UNIQUE (catalog_kind, catalog_id, slot_key) Neuer Katalog-Eintrag im Admin → **keine** Code-Änderung; Slots optional befüllen. +**Keine Migrations-Seeds:** Primärfokus und andere Stammdaten sind pro Umgebung unterschiedlich. Leere `catalog_prompt_slots` sind normal; zur Laufzeit greifen Namens-Fallbacks (`catalog_slot_fallbacks.py`) und die `description`-Spalte der Stammdaten. + --- ## 7. Laufzeit-Architektur @@ -162,7 +164,7 @@ Hardcodierte `SNIPPET_REGISTRY` — Proof of Concept für `catalog_guidance_bloc ### H2 — Slot-Modell (0.8.235) ✓ - [x] Tabellen `catalog_prompt_slot_types`, `catalog_prompt_slots` -- [x] Seed aus H1-Texten (Name-Match auf Stammdaten) +- [x] Seed aus H1-Texten (Name-Match auf Stammdaten) — **entfernt (095)**: keine Migrations-Seeds; Inhalte Admin oder `catalog_slot_fallbacks.py` - [x] Resolver mit granularen Platzhaltern + Aggregat - [x] Admin-API GET/PUT - [x] `SNIPPET_REGISTRY` aus Laufzeit-Pfad entfernt