Protokoll #2147

ID2147
Zeitstempel2025-12-21 10:11:42.687136
Clientroot
IP145.224.96.190
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens10,968 (Input: 2,650, Output: 8,318)
Dauer97 ms
Request-Zeit2025-12-21 10:11:42.687136
Response-Zeit2025-12-21 10:11:42.783704

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/docs\/plan-form-components-refactoring.md",
        "old_string": "## 0d. Context Limit (Hardcoded + LocalStorage)",
        "new_string": "## 0d. User-Präferenzen (Zentrale Defaults)\n\n### 0d.1 Architektur-Entscheidung\n\n**Gewählt: User-Profil in Datenbank**\n\n- Zentrale Tabelle `user_preferences`\n- Admin-Seite `\/ressourcen\/profil`\n- Sync über alle Geräte\n- Zukunftssicher für Multi-User\n\n### 0d.2 Datenbank-Tabelle `user_preferences`\n\n```sql\nCREATE TABLE user_preferences (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    user_id INT DEFAULT 1,                    -- Aktuell nur 1 User\n\n    -- LLM-Einstellungen\n    default_model_id VARCHAR(100),            -- FK → llm_models.model_id\n    default_temperature DECIMAL(3,2) DEFAULT 0.70,\n    default_max_tokens INT DEFAULT 4096,\n\n    -- RAG-Einstellungen\n    default_collections JSON,                 -- [\"documents\"]\n    default_context_limit INT DEFAULT 5,\n\n    -- Content-Einstellungen\n    default_author_profile_id INT,            -- FK → content_config.id\n    default_system_prompt_id INT,             -- FK → content_config.id\n    default_contract_id INT,                  -- FK → content_config.id\n    default_structure_id INT,                 -- FK → content_config.id\n\n    -- UI-Einstellungen\n    sidebar_collapsed BOOLEAN DEFAULT FALSE,\n    theme VARCHAR(20) DEFAULT 'system',       -- 'light', 'dark', 'system'\n\n    -- Timestamps\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n\n    UNIQUE KEY unique_user (user_id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\n-- Initial-Eintrag für User 1\nINSERT INTO user_preferences (user_id) VALUES (1);\n```\n\n### 0d.3 Impact-Analyse: Wer nutzt User-Präferenzen?\n\n#### Schreibend (Profil-Seite)\n\n| Komponente | Aktion |\n|------------|--------|\n| `\/ressourcen\/profil` | Alle Defaults bearbeiten |\n\n#### Lesend (Defaults anwenden)\n\n| Komponente | Wann | Was |\n|------------|------|-----|\n| **ChatController::createSession()** | Neue Session | model, collections, context_limit, temperature, max_tokens, author_profile, system_prompt |\n| **ChatController::show()** | View-Defaults | Dropdowns vorbelegen |\n| **ContentController::contentNew()** | Formular-Defaults | model, collections, context_limit, author_profile, contract, structure |\n| **ContentController::store()** | Fallback | Falls Feld leer |\n| **Layout** | UI | theme, sidebar_collapsed |\n| **Alle Partials** | Fallback | Default wenn kein Wert |\n\n### 0d.4 Werte-Hierarchie (Priorität)\n\n```\n1. Session\/Order-Wert     (existierende Daten)\n      ↓ falls null\n2. User-Präferenz         (user_preferences)\n      ↓ falls null\n3. System-Default         (hardcoded)\n```\n\n**Beispiel Chat:**\n```php\n$model = $session['model']                           \/\/ 1. Session\n      ?? $userPrefs['default_model_id']              \/\/ 2. User-Präferenz\n      ?? 'claude-sonnet-4-20250514';                 \/\/ 3. System-Default\n```\n\n### 0d.5 Repository & Service\n\n```php\n\/\/ \/src\/Infrastructure\/Persistence\/UserPreferencesRepository.php\nclass UserPreferencesRepository\n{\n    public function getForUser(int $userId = 1): array\n    {\n        $stmt = $this->db->prepare(\"\n            SELECT * FROM user_preferences WHERE user_id = ?\n        \");\n        $stmt->execute([$userId]);\n        return $stmt->fetch() ?: $this->getDefaults();\n    }\n\n    public function update(int $userId, array $data): void\n    {\n        \/\/ UPDATE user_preferences SET ... WHERE user_id = ?\n    }\n\n    private function getDefaults(): array\n    {\n        return [\n            'default_model_id' => null,\n            'default_temperature' => 0.70,\n            'default_max_tokens' => 4096,\n            'default_collections' => '[\"documents\"]',\n            'default_context_limit' => 5,\n            \/\/ ...\n        ];\n    }\n}\n```\n\n```php\n\/\/ \/src\/Infrastructure\/UserPreferencesService.php\nclass UserPreferencesService\n{\n    private UserPreferencesRepository $repository;\n    private ?array $cached = null;\n\n    public function get(): array\n    {\n        if ($this->cached === null) {\n            $this->cached = $this->repository->getForUser(1);\n        }\n        return $this->cached;\n    }\n\n    public function getDefault(string $key, mixed $fallback = null): mixed\n    {\n        $prefs = $this->get();\n        return $prefs[$key] ?? $fallback;\n    }\n}\n```\n\n### 0d.6 Controller-Integration\n\n#### Base Controller erweitern\n\n```php\n\/\/ \/src\/Framework\/Controller.php\nabstract class Controller\n{\n    protected UserPreferencesService $userPrefs;\n\n    public function __construct()\n    {\n        $this->userPrefs = new UserPreferencesService();\n    }\n\n    protected function view(string $template, array $data = []): void\n    {\n        \/\/ User-Präferenzen automatisch an alle Views\n        $data['userPrefs'] = $this->userPrefs->get();\n        \/\/ ...\n    }\n}\n```\n\n#### ChatController anpassen\n\n```php\n\/\/ ChatController::createSession()\nprivate function createSession(): string\n{\n    $prefs = $this->userPrefs->get();\n\n    $stmt = $this->db->prepare(\"\n        INSERT INTO chat_sessions\n        (uuid, model, collections, context_limit, temperature, max_tokens,\n         author_profile_id, system_prompt_id)\n        VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n    \");\n    $stmt->execute([\n        $uuid,\n        $prefs['default_model_id'] ?? 'claude-sonnet-4-20250514',\n        $prefs['default_collections'] ?? '[\"documents\"]',\n        $prefs['default_context_limit'] ?? 5,\n        $prefs['default_temperature'] ?? 0.70,\n        $prefs['default_max_tokens'] ?? 4096,\n        $prefs['default_author_profile_id'],\n        $prefs['default_system_prompt_id'],\n    ]);\n\n    return $uuid;\n}\n```\n\n#### ContentController anpassen\n\n```php\n\/\/ ContentController::contentNew()\npublic function contentNew(): void\n{\n    $prefs = $this->userPrefs->get();\n\n    $this->view('content.new', [\n        'models' => $this->llmRepository->getActive(),\n        'collections' => $this->collectionRepository->getSearchable(),\n        'defaultModel' => $prefs['default_model_id'],\n        'defaultCollections' => json_decode($prefs['default_collections'] ?? '[]', true),\n        'defaultContextLimit' => $prefs['default_context_limit'] ?? 5,\n        \/\/ ...\n    ]);\n}\n```\n\n### 0d.7 Admin-Seite `\/ressourcen\/profil`\n\n**URL:** `\/ressourcen\/profil`\n**Controller:** `RessourcenController::profilIndex()`\n\n**Layout:**\n\n```\n┌─────────────────────────────────────────────────────┐\n│ Mein Profil                                         │\n├─────────────────────────────────────────────────────┤\n│                                                     │\n│ LLM-Einstellungen                                   │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Standard-Modell │ │ Temperature     │            │\n│ │ [Claude Opus ▼] │ │ [====●====] 0.7 │            │\n│ └─────────────────┘ └─────────────────┘            │\n│ ┌─────────────────┐                                │\n│ │ Max Tokens      │                                │\n│ │ [4096 ▼]        │                                │\n│ └─────────────────┘                                │\n│                                                     │\n│ RAG-Einstellungen                                   │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Collections     │ │ Context Limit   │            │\n│ │ [■] Dokumente   │ │ [5 Quellen ▼]   │            │\n│ │ [ ] E-Mails     │ └─────────────────┘            │\n│ └─────────────────┘                                │\n│                                                     │\n│ Content-Einstellungen                               │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Autorenprofil   │ │ System Prompt   │            │\n│ │ [Akademisch ▼]  │ │ [Standard ▼]    │            │\n│ └─────────────────┘ └─────────────────┘            │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Contract        │ │ Struktur        │            │\n│ │ [Standard ▼]    │ │ [Artikel ▼]     │            │\n│ └─────────────────┘ └─────────────────┘            │\n│                                                     │\n│ UI-Einstellungen                                    │\n│ ┌─────────────────┐                                │\n│ │ Theme           │                                │\n│ │ [System ▼]      │                                │\n│ └─────────────────┘                                │\n│                                                     │\n│ [Speichern]                                         │\n└─────────────────────────────────────────────────────┘\n```\n\n### 0d.8 Partials mit User-Präferenzen\n\nAlle Partials erhalten `$userPrefs` automatisch via Controller:\n\n```php\n\/\/ \/src\/View\/partials\/form\/model-select.php\n<?php\n$models = $models ?? [];\n$selected = $selected                              \/\/ 1. Explizit übergeben\n         ?? $userPrefs['default_model_id']         \/\/ 2. User-Präferenz\n         ?? 'claude-sonnet-4-20250514';            \/\/ 3. Fallback\n\/\/ ...\n?>\n```\n\n```php\n\/\/ \/src\/View\/partials\/form\/context-limit.php\n<?php\n$selected = $selected\n         ?? $userPrefs['default_context_limit']\n         ?? 5;\n\/\/ ...\n?>\n```\n\n### 0d.9 LocalStorage als Cache (Optional)\n\nFür schnelleres Laden kann LocalStorage als Client-Cache dienen:\n\n```javascript\n\/\/ form-persistence.js\nconst userPrefs = JSON.parse(localStorage.getItem('userPrefs') || '{}');\n\n\/\/ Nach erfolgreichem Speichern auf \/ressourcen\/profil:\nlocalStorage.setItem('userPrefs', JSON.stringify(savedPrefs));\n```\n\n**Aber:** DB ist Source of Truth, LocalStorage nur Performance-Optimierung.\n\n### 0d.10 Migrations-Schritte (User-Präferenzen)\n\n#### Phase 1: Infrastruktur\n1. [ ] Tabelle `user_preferences` in `ki_dev` erstellen\n2. [ ] Initial-Eintrag für User 1 anlegen\n3. [ ] `UserPreferencesRepository` erstellen\n4. [ ] `UserPreferencesService` erstellen\n\n#### Phase 2: Base Controller\n5. [ ] `Controller.php` um `$userPrefs` erweitern\n6. [ ] Automatische Weitergabe an Views\n\n#### Phase 3: Admin-Seite\n7. [ ] Route `\/ressourcen\/profil` anlegen\n8. [ ] `RessourcenController::profilIndex()` implementieren\n9. [ ] `RessourcenController::profilUpdate()` implementieren\n10. [ ] View `\/ressourcen\/profil\/index.php` erstellen\n\n#### ... [TRUNCATED-6aed11aae639b604]"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/docs\/plan-form-components-refactoring.md",
        "oldString": "## 0d. Context Limit (Hardcoded + LocalStorage)",
        "newString": "## 0d. User-Präferenzen (Zentrale Defaults)\n\n### 0d.1 Architektur-Entscheidung\n\n**Gewählt: User-Profil in Datenbank**\n\n- Zentrale Tabelle `user_preferences`\n- Admin-Seite `\/ressourcen\/profil`\n- Sync über alle Geräte\n- Zukunftssicher für Multi-User\n\n### 0d.2 Datenbank-Tabelle `user_preferences`\n\n```sql\nCREATE TABLE user_preferences (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    user_id INT DEFAULT 1,                    -- Aktuell nur 1 User\n\n    -- LLM-Einstellungen\n    default_model_id VARCHAR(100),            -- FK → llm_models.model_id\n    default_temperature DECIMAL(3,2) DEFAULT 0.70,\n    default_max_tokens INT DEFAULT 4096,\n\n    -- RAG-Einstellungen\n    default_collections JSON,                 -- [\"documents\"]\n    default_context_limit INT DEFAULT 5,\n\n    -- Content-Einstellungen\n    default_author_profile_id INT,            -- FK → content_config.id\n    default_system_prompt_id INT,             -- FK → content_config.id\n    default_contract_id INT,                  -- FK → content_config.id\n    default_structure_id INT,                 -- FK → content_config.id\n\n    -- UI-Einstellungen\n    sidebar_collapsed BOOLEAN DEFAULT FALSE,\n    theme VARCHAR(20) DEFAULT 'system',       -- 'light', 'dark', 'system'\n\n    -- Timestamps\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n\n    UNIQUE KEY unique_user (user_id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\n-- Initial-Eintrag für User 1\nINSERT INTO user_preferences (user_id) VALUES (1);\n```\n\n### 0d.3 Impact-Analyse: Wer nutzt User-Präferenzen?\n\n#### Schreibend (Profil-Seite)\n\n| Komponente | Aktion |\n|------------|--------|\n| `\/ressourcen\/profil` | Alle Defaults bearbeiten |\n\n#### Lesend (Defaults anwenden)\n\n| Komponente | Wann | Was |\n|------------|------|-----|\n| **ChatController::createSession()** | Neue Session | model, collections, context_limit, temperature, max_tokens, author_profile, system_prompt |\n| **ChatController::show()** | View-Defaults | Dropdowns vorbelegen |\n| **ContentController::contentNew()** | Formular-Defaults | model, collections, context_limit, author_profile, contract, structure |\n| **ContentController::store()** | Fallback | Falls Feld leer |\n| **Layout** | UI | theme, sidebar_collapsed |\n| **Alle Partials** | Fallback | Default wenn kein Wert |\n\n### 0d.4 Werte-Hierarchie (Priorität)\n\n```\n1. Session\/Order-Wert     (existierende Daten)\n      ↓ falls null\n2. User-Präferenz         (user_preferences)\n      ↓ falls null\n3. System-Default         (hardcoded)\n```\n\n**Beispiel Chat:**\n```php\n$model = $session['model']                           \/\/ 1. Session\n      ?? $userPrefs['default_model_id']              \/\/ 2. User-Präferenz\n      ?? 'claude-sonnet-4-20250514';                 \/\/ 3. System-Default\n```\n\n### 0d.5 Repository & Service\n\n```php\n\/\/ \/src\/Infrastructure\/Persistence\/UserPreferencesRepository.php\nclass UserPreferencesRepository\n{\n    public function getForUser(int $userId = 1): array\n    {\n        $stmt = $this->db->prepare(\"\n            SELECT * FROM user_preferences WHERE user_id = ?\n        \");\n        $stmt->execute([$userId]);\n        return $stmt->fetch() ?: $this->getDefaults();\n    }\n\n    public function update(int $userId, array $data): void\n    {\n        \/\/ UPDATE user_preferences SET ... WHERE user_id = ?\n    }\n\n    private function getDefaults(): array\n    {\n        return [\n            'default_model_id' => null,\n            'default_temperature' => 0.70,\n            'default_max_tokens' => 4096,\n            'default_collections' => '[\"documents\"]',\n            'default_context_limit' => 5,\n            \/\/ ...\n        ];\n    }\n}\n```\n\n```php\n\/\/ \/src\/Infrastructure\/UserPreferencesService.php\nclass UserPreferencesService\n{\n    private UserPreferencesRepository $repository;\n    private ?array $cached = null;\n\n    public function get(): array\n    {\n        if ($this->cached === null) {\n            $this->cached = $this->repository->getForUser(1);\n        }\n        return $this->cached;\n    }\n\n    public function getDefault(string $key, mixed $fallback = null): mixed\n    {\n        $prefs = $this->get();\n        return $prefs[$key] ?? $fallback;\n    }\n}\n```\n\n### 0d.6 Controller-Integration\n\n#### Base Controller erweitern\n\n```php\n\/\/ \/src\/Framework\/Controller.php\nabstract class Controller\n{\n    protected UserPreferencesService $userPrefs;\n\n    public function __construct()\n    {\n        $this->userPrefs = new UserPreferencesService();\n    }\n\n    protected function view(string $template, array $data = []): void\n    {\n        \/\/ User-Präferenzen automatisch an alle Views\n        $data['userPrefs'] = $this->userPrefs->get();\n        \/\/ ...\n    }\n}\n```\n\n#### ChatController anpassen\n\n```php\n\/\/ ChatController::createSession()\nprivate function createSession(): string\n{\n    $prefs = $this->userPrefs->get();\n\n    $stmt = $this->db->prepare(\"\n        INSERT INTO chat_sessions\n        (uuid, model, collections, context_limit, temperature, max_tokens,\n         author_profile_id, system_prompt_id)\n        VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n    \");\n    $stmt->execute([\n        $uuid,\n        $prefs['default_model_id'] ?? 'claude-sonnet-4-20250514',\n        $prefs['default_collections'] ?? '[\"documents\"]',\n        $prefs['default_context_limit'] ?? 5,\n        $prefs['default_temperature'] ?? 0.70,\n        $prefs['default_max_tokens'] ?? 4096,\n        $prefs['default_author_profile_id'],\n        $prefs['default_system_prompt_id'],\n    ]);\n\n    return $uuid;\n}\n```\n\n#### ContentController anpassen\n\n```php\n\/\/ ContentController::contentNew()\npublic function contentNew(): void\n{\n    $prefs = $this->userPrefs->get();\n\n    $this->view('content.new', [\n        'models' => $this->llmRepository->getActive(),\n        'collections' => $this->collectionRepository->getSearchable(),\n        'defaultModel' => $prefs['default_model_id'],\n        'defaultCollections' => json_decode($prefs['default_collections'] ?? '[]', true),\n        'defaultContextLimit' => $prefs['default_context_limit'] ?? 5,\n        \/\/ ...\n    ]);\n}\n```\n\n### 0d.7 Admin-Seite `\/ressourcen\/profil`\n\n**URL:** `\/ressourcen\/profil`\n**Controller:** `RessourcenController::profilIndex()`\n\n**Layout:**\n\n```\n┌─────────────────────────────────────────────────────┐\n│ Mein Profil                                         │\n├─────────────────────────────────────────────────────┤\n│                                                     │\n│ LLM-Einstellungen                                   │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Standard-Modell │ │ Temperature     │            │\n│ │ [Claude Opus ▼] │ │ [====●====] 0.7 │            │\n│ └─────────────────┘ └─────────────────┘            │\n│ ┌─────────────────┐                                │\n│ │ Max Tokens      │                                │\n│ │ [4096 ▼]        │                                │\n│ └─────────────────┘                                │\n│                                                     │\n│ RAG-Einstellungen                                   │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Collections     │ │ Context Limit   │            │\n│ │ [■] Dokumente   │ │ [5 Quellen ▼]   │            │\n│ │ [ ] E-Mails     │ └─────────────────┘            │\n│ └─────────────────┘                                │\n│                                                     │\n│ Content-Einstellungen                               │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Autorenprofil   │ │ System Prompt   │            │\n│ │ [Akademisch ▼]  │ │ [Standard ▼]    │            │\n│ └─────────────────┘ └─────────────────┘            │\n│ ┌─────────────────┐ ┌─────────────────┐            │\n│ │ Contract        │ │ Struktur        │            │\n│ │ [Standard ▼]    │ │ [Artikel ▼]     │            │\n│ └─────────────────┘ └─────────────────┘            │\n│                                                     │\n│ UI-Einstellungen                                    │\n│ ┌─────────────────┐                                │\n│ │ Theme           │                                │\n│ │ [System ▼]      │                                │\n│ └─────────────────┘                                │\n│                                                     │\n│ [Speichern]                                         │\n└─────────────────────────────────────────────────────┘\n```\n\n### 0d.8 Partials mit User-Präferenzen\n\nAlle Partials erhalten `$userPrefs` automatisch via Controller:\n\n```php\n\/\/ \/src\/View\/partials\/form\/model-select.php\n<?php\n$models = $models ?? [];\n$selected = $selected                              \/\/ 1. Explizit übergeben\n         ?? $userPrefs['default_model_id']         \/\/ 2. User-Präferenz\n         ?? 'claude-sonnet-4-20250514';            \/\/ 3. Fallback\n\/\/ ...\n?>\n```\n\n```php\n\/\/ \/src\/View\/partials\/form\/context-limit.php\n<?php\n$selected = $selected\n         ?? $userPrefs['default_context_limit']\n         ?? 5;\n\/\/ ...\n?>\n```\n\n### 0d.9 LocalStorage als Cache (Optional)\n\nFür schnelleres Laden kann LocalStorage als Client-Cache dienen:\n\n```javascript\n\/\/ form-persistence.js\nconst userPrefs = JSON.parse(localStorage.getItem('userPrefs') || '{}');\n\n\/\/ Nach erfolgreichem Speichern auf \/ressourcen\/profil:\nlocalStorage.setItem('userPrefs', JSON.stringify(savedPrefs));\n```\n\n**Aber:** DB ist Source of Truth, LocalStorage nur Performance-Optimierung.\n\n### 0d.10 Migrations-Schritte (User-Präferenzen)\n\n#### Phase 1: Infrastruktur\n1. [ ] Tabelle `user_preferences` in `ki_dev` erstellen\n2. [ ] Initial-Eintrag für User 1 anlegen\n3. [ ] `UserPreferencesRepository` erstellen\n4. [ ] `UserPreferencesService` erstellen\n\n#### Phase 2: Base Controller\n5. [ ] `Controller.php` um `$userPrefs` erweitern\n6. [ ] Automatische Weitergabe an Views\n\n#### Phase 3: Admin-Seite\n7. [ ] Route `\/ressourcen\/profil` anlegen\n8. [ ] `RessourcenController::profilIndex()` implementieren\n9. [ ] `RessourcenController::profilUpdate()` implementieren\n10. [ ] View `\/ressourcen\/profil\/index.php` erstellen\n\n#### ... [TRUNCATED-6aed11aae639b604]",
        "originalFile": "# Plan: Einheitliche Form-Komponenten (Chat & Content Studio)\n\n## Status: PLANUNG\n**Erstellt:** 2025-12-21\n**Ziel:** DRY, KISS, SRP - Shared Partials für alle wiederverwendbaren Form-Elemente\n\n---\n\n## 0. LLM-Verwaltung (Grundlage für Modell-Dropdown)\n\n### 0.1 Architektur-Entscheidung\n\n**Gewählt: Variante B - Dedizierte LLM-Verwaltung**\n\nStatt dynamischer API-Abfrage bei jedem Seitenaufruf:\n- Zentrale Admin-Seite `\/ressourcen\/llm`\n- Datenbank-Tabelle als Single Source of Truth\n- Sync-Buttons für Provider (Anthropic, Ollama, weitere)\n- Sprechende Namen und Zusatzmetadaten\n\n### 0.2 Datenbank-Tabelle `llm_models`\n\n```sql\nCREATE TABLE llm_models (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    provider ENUM('anthropic', 'ollama', 'openai', 'google', 'mistral', 'custom') NOT NULL,\n    model_id VARCHAR(100) NOT NULL,        -- API-ID: \"claude-opus-4-5-20251101\"\n    display_name VARCHAR(100) NOT NULL,    -- Anzeige: \"Claude Opus 4.5\"\n    description TEXT,                      -- Kurzbeschreibung\n    context_window INT,                    -- Max. Tokens Input: 200000\n    max_output_tokens INT,                 -- Max. Tokens Output: 8192\n    input_price_per_mtok DECIMAL(10,4),    -- Preis Input $\/MTok\n    output_price_per_mtok DECIMAL(10,4),   -- Preis Output $\/MTok\n    capabilities JSON,                     -- {\"vision\": true, \"function_calling\": true}\n    is_local BOOLEAN DEFAULT FALSE,        -- Lokal (Ollama) vs. Cloud\n    is_active BOOLEAN DEFAULT TRUE,        -- In Dropdowns anzeigen?\n    sort_order INT DEFAULT 0,              -- Reihenfolge in Dropdowns\n    last_synced_at DATETIME,               -- Letzte Synchronisation\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    UNIQUE KEY unique_provider_model (provider, model_id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n```\n\n### 0.3 Admin-Seite `\/ressourcen\/llm`\n\n**URL:** `\/ressourcen\/llm`\n**Controller:** `RessourcenController::llmIndex()`\n\n**Funktionen:**\n\n| Button | Aktion |\n|--------|--------|\n| \"Anthropic synchronisieren\" | `GET api.anthropic.com\/v1\/models` → DB aktualisieren |\n| \"Ollama synchronisieren\" | `ollama list` → DB aktualisieren |\n| \"Neuer Provider\" | Manuell weiteren Provider hinzufügen |\n\n**Tabellen-Ansicht:**\n\n| Provider | Model-ID | Display-Name | Context | Preis In\/Out | Lokal | Aktiv | Aktionen |\n|----------|----------|--------------|---------|--------------|-------|-------|----------|\n| Anthropic | claude-opus-4-5-20251101 | Claude Opus 4.5 | 200k | $15\/$75 | - | ✓ | Bearbeiten |\n| Ollama | mistral:latest | Mistral 7B | 32k | - | ✓ | ✓ | Bearbeiten |\n\n**Bearbeiten-Dialog:**\n- Display-Name ändern\n- Beschreibung hinzufügen\n- Context-Window \/ Max-Output korrigieren\n- Preise eintragen (für Kostenberechnung)\n- Aktivieren\/Deaktivieren\n- Sortierung ändern\n\n### 0.4 Sync-Logik\n\n**Anthropic Sync:**\n```php\n\/\/ GET https:\/\/api.anthropic.com\/v1\/models\n\/\/ Response: {\"data\": [{\"id\": \"claude-opus-4-5-20251101\", ...}]}\n\nforeach ($apiModels as $model) {\n    \/\/ INSERT ... ON DUPLICATE KEY UPDATE\n    \/\/ Neue Modelle: is_active = true, display_name = model_id (initial)\n    \/\/ Existierende: last_synced_at aktualisieren\n    \/\/ Fehlende: NICHT löschen, nur last_synced_at bleibt alt\n}\n```\n\n**Ollama Sync:**\n```php\n\/\/ $ ollama list\n\/\/ NAME              ID           SIZE    MODIFIED\n\/\/ mistral:latest    abc123...    4.1 GB  2 days ago\n\n$output = shell_exec('ollama list');\n\/\/ Parsen und in DB einfügen\n```\n\n### 0.5 Dropdown-Query\n\n```php\n\/\/ ModelService::getActiveModels()\npublic function getActiveModels(): array\n{\n    return $this->db->query(\"\n        SELECT model_id, display_name, provider, is_local, context_window\n        FROM llm_models\n        WHERE is_active = 1\n        ORDER BY is_local ASC, sort_order ASC, display_name ASC\n    \")->fetchAll();\n}\n```\n\n### 0.6 Partial nutzt DB-Daten\n\n```php\n\/\/ \/src\/View\/partials\/form\/model-select.php\n<?php\n$models = $models ?? [];\n$selected = $selected ?? '';\n$variant = $variant ?? 'default';\n$class = $variant === 'inline' ? 'form-select--inline' : 'form-select';\n?>\n<select name=\"model\" id=\"model\" class=\"<?= $class ?>\">\n    <optgroup label=\"Cloud\">\n        <?php foreach ($models as $m): ?>\n        <?php if (!$m['is_local']): ?>\n        <option value=\"<?= $m['model_id'] ?>\" <?= $selected === $m['model_id'] ? 'selected' : '' ?>>\n            <?= htmlspecialchars($m['display_name']) ?>\n        <\/option>\n        <?php endif; ?>\n        <?php endforeach; ?>\n    <\/optgroup>\n    <optgroup label=\"Lokal\">\n        <?php foreach ($models as $m): ?>\n        <?php if ($m['is_local']): ?>\n        <option value=\"<?= $m['model_id'] ?>\" <?= $selected === $m['model_id'] ? 'selected' : '' ?>>\n            <?= htmlspecialchars($m['display_name']) ?>\n        <\/option>\n        <?php endif; ?>\n        <?php endforeach; ?>\n    <\/optgroup>\n<\/select>\n```\n\n### 0.7 Migrations-Schritte (LLM-Verwaltung)\n\n1. [ ] Tabelle `llm_models` in `ki_dev` erstellen\n2. [ ] `LlmModelRepository` erstellen\n3. [ ] `LlmSyncService` erstellen (Anthropic + Ollama)\n4. [ ] Route `\/ressourcen\/llm` anlegen\n5. [ ] `RessourcenController::llmIndex()` implementieren\n6. [ ] View `\/ressourcen\/llm\/index.php` erstellen\n7. [ ] Sync-Buttons implementieren\n8. [ ] Bearbeiten-Funktionalität\n9. [ ] Initial-Sync durchführen (bestehende Modelle importieren)\n10. [ ] `ModelConfig.php` durch `LlmModelRepository` ersetzen\n11. [ ] Partial `model-select.php` erstellen\n12. [ ] Chat und Content auf Partial umstellen\n\n---\n\n## 0b. Collection-Verwaltung (Grundlage für Collection-Dropdown)\n\n### 0b.1 Architektur-Entscheidung\n\n**Gewählt: DB-Tabelle analog zu LLMs**\n\n- Zentrale Admin-Seite `\/ressourcen\/collections`\n- Datenbank-Tabelle als Single Source of Truth\n- Sync mit Qdrant (Metadaten abrufen)\n- Sprechende Namen und Beschreibungen\n- **Multi-Select** als einheitliche Darstellung\n\n### 0b.2 Datenbank-Tabelle `rag_collections`\n\n```sql\nCREATE TABLE rag_collections (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    collection_id VARCHAR(100) NOT NULL UNIQUE,  -- Qdrant-Name: \"documents\"\n    display_name VARCHAR(100) NOT NULL,          -- Anzeige: \"Dokumente\"\n    description TEXT,                            -- \"PDF-Dokumente aus Nextcloud\"\n\n    -- Qdrant-Metadaten (via Sync)\n    vector_size INT,                             -- 1024\n    distance_metric VARCHAR(20),                 -- \"Cosine\"\n    points_count INT DEFAULT 0,                  -- Anzahl Vektoren\n\n    -- Konfiguration\n    embedding_model VARCHAR(100),                -- \"mxbai-embed-large\"\n    chunk_size INT,                              -- 2000\n    chunk_overlap INT,                           -- 200\n\n    -- Verwaltung\n    source_type ENUM('nextcloud', 'mail', 'manual', 'system') DEFAULT 'manual',\n    source_path VARCHAR(500),                    -- \"\/var\/www\/nextcloud\/data\/...\"\n    is_active BOOLEAN DEFAULT TRUE,              -- In Dropdowns anzeigen?\n    is_searchable BOOLEAN DEFAULT TRUE,          -- Für RAG verfügbar?\n    sort_order INT DEFAULT 0,\n\n    -- Timestamps\n    last_synced_at DATETIME,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n```\n\n### 0b.3 Impact-Analyse: Wer nutzt Collections?\n\n| System\/Seite | Aktueller Zugriff | Neuer Zugriff | Änderung |\n|--------------|-------------------|---------------|----------|\n| **Chat** | `$qdrantService->listCollections()` | `CollectionRepository::getActive()` | Query aus DB |\n| **Content Studio** | `$qdrantService->listCollections()` | `CollectionRepository::getActive()` | Query aus DB |\n| **Content Show** | `$qdrantService->listCollections()` | `CollectionRepository::getActive()` | Query aus DB |\n| **Pipeline (Python)** | `config.py QDRANT_COLLECTIONS` | DB-Query oder Config-Sync | Python liest DB |\n| **Semantic Explorer** | Direkt Qdrant | `CollectionRepository` + Qdrant | Metadaten aus DB |\n| **System Explorer** | - | Statistiken aus DB | Neu |\n| **API \/api\/v1\/search** | Qdrant direkt | Validierung gegen DB | Nur aktive Collections |\n| **Embedding-Service** | `config.py` | DB oder Sync | Konsistenz |\n\n### 0b.4 Admin-Seite `\/ressourcen\/collections`\n\n**URL:** `\/ressourcen\/collections`\n**Controller:** `RessourcenController::collectionsIndex()`\n\n**Funktionen:**\n\n| Button | Aktion |\n|--------|--------|\n| \"Qdrant synchronisieren\" | Collections + Metadaten von Qdrant abrufen |\n| \"Neue Collection\" | Manuell Collection registrieren |\n\n**Tabellen-Ansicht:**\n\n| Collection-ID | Display-Name | Vektoren | Größe | Quelle | Aktiv | Suchbar | Aktionen |\n|---------------|--------------|----------|-------|--------|-------|---------|----------|\n| documents | Dokumente | 1.247 | 1024d | Nextcloud | ✓ | ✓ | Bearbeiten |\n| dokumentation_chunks | Doku-Chunks | 892 | 1024d | System | ✓ | ✓ | Bearbeiten |\n| mail | E-Mails | 3.421 | 1024d | Mail | ✓ | ✓ | Bearbeiten |\n| entities | Entitäten | 156 | 1024d | System | ✓ | - | Bearbeiten |\n\n**Bearbeiten-Dialog:**\n- Display-Name ändern\n- Beschreibung hinzufügen\n- Quelle\/Pfad dokumentieren\n- Aktivieren\/Deaktivieren (Dropdown)\n- Suchbar ja\/nein (RAG-Verfügbarkeit)\n- Sortierung ändern\n\n### 0b.5 Sync-Logik\n\n**Qdrant Sync:**\n```php\n\/\/ QdrantService::getCollectionsInfo()\npublic function syncCollections(): array\n{\n    $qdrantCollections = $this->client->getCollections();\n\n    foreach ($qdrantCollections as $col) {\n        $info = $this->client->getCollectionInfo($col['name']);\n\n        \/\/ INSERT ... ON DUPLICATE KEY UPDATE\n        $this->repository->upsert([\n            'collection_id' => $col['name'],\n            'display_name' => $col['name'],  \/\/ Initial = ID\n            'vector_size' => $info['config']['params']['size'],\n            'distance_metric' => $info['config']['params']['distance'],\n            'points_count' => $info['points_count'],\n            'last_synced_at' => now(),\n        ]);\n    }\n}\n```\n\n### 0b.6 Repository-Interface\n\n```php\n\/\/ CollectionRepository.php\ninterface CollectionRepositoryInterfac... [TRUNCATED-2c51be340914f793]",
        "structuredPatch": [
            {
                "oldStart": 379,
                "oldLines": 10,
                "newStart": 379,
                "newLines": 348,
                "lines": [
                    " ",
                    " ---",
                    " ",
                    "-## 0d. Context Limit (Hardcoded + LocalStorage)",
                    "+## 0d. User-Präferenzen (Zentrale Defaults)",
                    " ",
                    " ### 0d.1 Architektur-Entscheidung",
                    " ",
                    "+**Gewählt: User-Profil in Datenbank**",
                    "+",
                    "+- Zentrale Tabelle `user_preferences`",
                    "+- Admin-Seite `\/ressourcen\/profil`",
                    "+- Sync über alle Geräte",
                    "+- Zukunftssicher für Multi-User",
                    "+",
                    "+### 0d.2 Datenbank-Tabelle `user_preferences`",
                    "+",
                    "+```sql",
                    "+CREATE TABLE user_preferences (",
                    "+    id INT AUTO_INCREMENT PRIMARY KEY,",
                    "+    user_id INT DEFAULT 1,                    -- Aktuell nur 1 User",
                    "+",
                    "+    -- LLM-Einstellungen",
                    "+    default_model_id VARCHAR(100),            -- FK → llm_models.model_id",
                    "+    default_temperature DECIMAL(3,2) DEFAULT 0.70,",
                    "+    default_max_tokens INT DEFAULT 4096,",
                    "+",
                    "+    -- RAG-Einstellungen",
                    "+    default_collections JSON,                 -- [\"documents\"]",
                    "+    default_context_limit INT DEFAULT 5,",
                    "+",
                    "+    -- Content-Einstellungen",
                    "+    default_author_profile_id INT,            -- FK → content_config.id",
                    "+    default_system_prompt_id INT,             -- FK → content_config.id",
                    "+    default_contract_id INT,                  -- FK → content_config.id",
                    "+    default_structure_id INT,                 -- FK → content_config.id",
                    "+",
                    "+    -- UI-Einstellungen",
                    "+    sidebar_collapsed BOOLEAN DEFAULT FALSE,",
                    "+    theme VARCHAR(20) DEFAULT 'system',       -- 'light', 'dark', 'system'",
                    "+",
                    "+    -- Timestamps",
                    "+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,",
                    "+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,",
                    "+",
                    "+    UNIQUE KEY unique_user (user_id)",
                    "+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
                    "+",
                    "+-- Initial-Eintrag für User 1",
                    "+INSERT INTO user_preferences (user_id) VALUES (1);",
                    "+```",
                    "+",
                    "+### 0d.3 Impact-Analyse: Wer nutzt User-Präferenzen?",
                    "+",
                    "+#### Schreibend (Profil-Seite)",
                    "+",
                    "+| Komponente | Aktion |",
                    "+|------------|--------|",
                    "+| `\/ressourcen\/profil` | Alle Defaults bearbeiten |",
                    "+",
                    "+#### Lesend (Defaults anwenden)",
                    "+",
                    "+| Komponente | Wann | Was |",
                    "+|------------|------|-----|",
                    "+| **ChatController::createSession()** | Neue Session | model, collections, context_limit, temperature, max_tokens, author_profile, system_prompt |",
                    "+| **ChatController::show()** | View-Defaults | Dropdowns vorbelegen |",
                    "+| **ContentController::contentNew()** | Formular-Defaults | model, collections, context_limit, author_profile, contract, structure |",
                    "+| **ContentController::store()** | Fallback | Falls Feld leer |",
                    "+| **Layout** | UI | theme, sidebar_collapsed |",
                    "+| **Alle Partials** | Fallback | Default wenn kein Wert |",
                    "+",
                    "+### 0d.4 Werte-Hierarchie (Priorität)",
                    "+",
                    "+```",
                    "+1. Session\/Order-Wert     (existierende Daten)",
                    "+      ↓ falls null",
                    "+2. User-Präferenz         (user_preferences)",
                    "+      ↓ falls null",
                    "+3. System-Default         (hardcoded)",
                    "+```",
                    "+",
                    "+**Beispiel Chat:**",
                    "+```php",
                    "+$model = $session['model']                           \/\/ 1. Session",
                    "+      ?? $userPrefs['default_model_id']              \/\/ 2. User-Präferenz",
                    "+      ?? 'claude-sonnet-4-20250514';                 \/\/ 3. System-Default",
                    "+```",
                    "+",
                    "+### 0d.5 Repository & Service",
                    "+",
                    "+```php",
                    "+\/\/ \/src\/Infrastructure\/Persistence\/UserPreferencesRepository.php",
                    "+class UserPreferencesRepository",
                    "+{",
                    "+    public function getForUser(int $userId = 1): array",
                    "+    {",
                    "+        $stmt = $this->db->prepare(\"",
                    "+            SELECT * FROM user_preferences WHERE user_id = ?",
                    "+        \");",
                    "+        $stmt->execute([$userId]);",
                    "+        return $stmt->fetch() ?: $this->getDefaults();",
                    "+    }",
                    "+",
                    "+    public function update(int $userId, array $data): void",
                    "+    {",
                    "+        \/\/ UPDATE user_preferences SET ... WHERE user_id = ?",
                    "+    }",
                    "+",
                    "+    private function getDefaults(): array",
                    "+    {",
                    "+        return [",
                    "+            'default_model_id' => null,",
                    "+            'default_temperature' => 0.70,",
                    "+            'default_max_tokens' => 4096,",
                    "+            'default_collections' => '[\"documents\"]',",
                    "+            'default_context_limit' => 5,",
                    "+            \/\/ ...",
                    "+        ];",
                    "+    }",
                    "+}",
                    "+```",
                    "+",
                    "+```php",
                    "+\/\/ \/src\/Infrastructure\/UserPreferencesService.php",
                    "+class UserPreferencesService",
                    "+{",
                    "+    private UserPreferencesRepository $repository;",
                    "+    private ?array $cached = null;",
                    "+",
                    "+    public function get(): array",
                    "+    {",
                    "+        if ($this->cached === null) {",
                    "+            $this->cached = $this->repository->getForUser(1);",
                    "+        }",
                    "+        return $this->cached;",
                    "+    }",
                    "+",
                    "+    public function getDefault(string $key, mixed $fallback = null): mixed",
                    "+    {",
                    "+        $prefs = $this->get();",
                    "+        return $prefs[$key] ?? $fallback;",
                    "+    }",
                    "+}",
                    "+```",
                    "+",
                    "+### 0d.6 Controller-Integration",
                    "+",
                    "+#### Base Controller erweitern",
                    "+",
                    "+```php",
                    "+\/\/ \/src\/Framework\/Controller.php",
                    "+abstract class Controller",
                    "+{",
                    "+    protected UserPreferencesService $userPrefs;",
                    "+",
                    "+    public function __construct()",
                    "+    {",
                    "+        $this->userPrefs = new UserPreferencesService();",
                    "+    }",
                    "+",
                    "+    protected function view(string $template, array $data = []): void",
                    "+    {",
                    "+        \/\/ User-Präferenzen automatisch an alle Views",
                    "+        $data['userPrefs'] = $this->userPrefs->get();",
                    "+        \/\/ ...",
                    "+    }",
                    "+}",
                    "+```",
                    "+",
                    "+#### ChatController anpassen",
                    "+",
                    "+```php",
                    "+\/\/ ChatController::createSession()",
                    "+private function createSession(): string",
                    "+{",
                    "+    $prefs = $this->userPrefs->get();",
                    "+",
                    "+    $stmt = $this->db->prepare(\"",
                    "+        INSERT INTO chat_sessions",
                    "+        (uuid, model, collections, context_limit, temperature, max_tokens,",
                    "+         author_profile_id, system_prompt_id)",
                    "+        VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
                    "+    \");",
                    "+    $stmt->execute([",
                    "+        $uuid,",
                    "+        $prefs['default_model_id'] ?? 'claude-sonnet-4-20250514',",
                    "+        $prefs['default_collections'] ?? '[\"documents\"]',",
                    "+        $prefs['default_context_limit'] ?? 5,",
                    "+        $prefs['default_temperature'] ?? 0.70,",
                    "+        $prefs['default_max_tokens'] ?? 4096,",
                    "+        $prefs['default_author_profile_id'],",
                    "+        $prefs['default_system_prompt_id'],",
                    "+    ]);",
                    "+",
                    "+    return $uuid;",
                    "+}",
                    "+```",
                    "+",
                    "+#### ContentController anpassen",
                    "+",
                    "+```php",
                    "+\/\/ ContentController::contentNew()",
                    "+public function contentNew(): void",
                    "+{",
                    "+    $prefs = $this->userPrefs->get();",
                    "+",
                    "+    $this->view('content.new', [",
                    "+        'models' => $this->llmRepository->getActive(),",
                    "+        'collections' => $this->collectionRepository->getSearchable(),",
                    "+        'defaultModel' => $prefs['default_model_id'],",
                    "+        'defaultCollections' => json_decode($prefs['default_collections'] ?? '[]', true),",
                    "+        'defaultContextLimit' => $prefs['default_context_limit'] ?? 5,",
                    "+        \/\/ ...",
                    "+    ]);",
                    "+}",
                    "+```",
                    "+",
                    "+### 0d.7 Admin-Seite `\/ressourcen\/profil`",
                    "+",
                    "+**URL:** `\/ressourcen\/profil`",
                    "+**Controller:** `RessourcenController::profilIndex()`",
                    "+",
                    "+**Layout:**",
                    "+",
                    "+```",
                    "+┌─────────────────────────────────────────────────────┐",
                    "+│ Mein Profil                                         │",
                    "+├─────────────────────────────────────────────────────┤",
                    "+│                                                     │",
                    "+│ LLM-Einstellungen                                   │",
                    "+│ ┌─────────────────┐ ┌─────────────────┐            │",
                    "+│ │ Standard-Modell │ │ Temperature     │            │",
                    "+│ │ [Claude Opus ▼] │ │ [====●====] 0.7 │            │",
                    "+│ └─────────────────┘ └─────────────────┘            │",
                    "+│ ┌─────────────────┐                                │",
                    "+│ │ Max Tokens      │                                │",
                    "+│ │ [4096 ▼]        │                                │",
                    "+│ └─────────────────┘                                │",
                    "+│                                                     │",
                    "+│ RAG-Einstellungen                                   │",
                    "+│ ┌─────────────────┐ ┌─────────────────┐            │",
                    "+│ │ Collections     │ │ Context Limit   │            │",
                    "+│ │ [■] Dokumente   │ │ [5 Quellen ▼]   │            │",
                    "+│ │ [ ] E-Mails     │ └─────────────────┘            │",
                    "+│ └─────────────────┘                                │",
                    "+│                                                     │",
                    "+│ Content-Einstellungen                               │",
                    "+│ ┌─────────────────┐ ┌─────────────────┐            │",
                    "+│ │ Autorenprofil   │ │ System Prompt   │            │",
                    "+│ │ [Akademisch ▼]  │ │ [Standard ▼]    │            │",
                    "+│ └─────────────────┘ └─────────────────┘            │",
                    "+│ ┌─────────────────┐ ┌─────────────────┐            │",
                    "+│ │ Contract        │ │ Struktur        │            │",
                    "+│ │ [Standard ▼]    │ │ [Artikel ▼]     │            │",
                    "+│ └─────────────────┘ └─────────────────┘            │",
                    "+│                                                     │",
                    "+│ UI-Einstellungen                                    │",
                    "+│ ┌─────────────────┐                                │",
                    "+│ │ Theme           │                                │",
                    "+│ │ [System ▼]      │                                │",
                    "+│ └─────────────────┘                                │",
                    "+│                                                     │",
                    "+│ [Speichern]                                         │",
                    "+└─────────────────────────────────────────────────────┘",
                    "+```",
                    "+",
                    "+### 0d.8 Partials mit User-Präferenzen",
                    "+",
                    "+Alle Partials erhalten `$userPrefs` automatisch via Controller:",
                    "+",
                    "+```php",
                    "+\/\/ \/src\/View\/partials\/form\/model-select.php",
                    "+<?php",
                    "+$models = $models ?? [];",
                    "+$selected = $selected                              \/\/ 1. Explizit übergeben",
                    "+         ?? $userPrefs['default_model_id']         \/\/ 2. User-Präferenz",
                    "+         ?? 'claude-sonnet-4-20250514';            \/\/ 3. Fallback",
                    "+\/\/ ...",
                    "+?>",
                    "+```",
                    "+",
                    "+```php",
                    "+\/\/ \/src\/View\/partials\/form\/context-limit.php",
                    "+<?php",
                    "+$selected = $selected",
                    "+         ?? $userPrefs['default_context_limit']",
                    "+         ?? 5;",
                    "+\/\/ ...",
                    "+?>",
                    "+```",
                    "+",
                    "+### 0d.9 LocalStorage als Cache (Optional)",
                    "+",
                    "+Für schnelleres Laden kann LocalStorage als Client-Cache dienen:",
                    "+",
                    "+```javascript",
                    "+\/\/ form-persistence.js",
                    "+const userPrefs = JSON.parse(localStorage.getItem('userPrefs') || '{}');",
                    "+",
                    "+\/\/ Nach erfolgreichem Speichern auf \/ressourcen\/profil:",
                    "+localStorage.setItem('userPrefs', JSON.stringify(savedPrefs));",
                    "+```",
                    "+",
                    "+**Aber:** DB ist Source of Truth, LocalStorage nur Performance-Optimierung.",
                    "+",
                    "+### 0d.10 Migrations-Schritte (User-Präferenzen)",
                    "+",
                    "+#### Phase 1: Infrastruktur",
                    "+1. [ ] Tabelle `user_preferences` in `ki_dev` erstellen",
                    "+2. [ ] Initial-Eintrag für User 1 anlegen",
                    "+3. [ ] `UserPreferencesRepository` erstellen",
                    "+4. [ ] `UserPreferencesService` erstellen",
                    "+",
                    "+#### Phase 2: Base Controller",
                    "+5. [ ] `Controller.php` um `$userPrefs` erweitern",
                    "+6. [ ] Automatische Weitergabe an Views",
                    "+",
                    "+#### Phase 3: Admin-Seite",
                    "+7. [ ] Route `\/ressourcen\/profil` anlegen",
                    "+8. [ ] `RessourcenController::profilIndex()` implementieren",
                    "+9. [ ] `RessourcenController::profilUpdate()` implementieren",
                    "+10. [ ] View `\/ressourcen\/profil\/index.php` erstellen",
                    "+",
                    "+#### Phase 4: Integration Chat",
                    "+11. [ ] `ChatController::createSession()` anpassen",
                    "+12. [ ] `ChatController::show()` Defaults aus Prefs",
                    "+",
                    "+#### Phase 5: Integration Content",
                    "+13. [ ] `ContentController::contentNew()` anpassen",
                    "+14. [ ] `ContentController::store()` Fallbacks",
                    "+",
                    "+#### Phase 6: Partials",
                    "+15. [ ] Alle Partials auf `$userPrefs`-Fallback umstellen",
                    "+",
                    "+#### Phase 7: Testing",
                    "+16. [ ] Profil-Seite: Speichern funktioniert",
                    "+17. [ ] Neue Chat-Session: Übernimmt Defaults",
                    "+18. [ ] Neuer Content: Übernimmt Defaults",
                    "+19. [ ] Bestehende Session: Behält eigene Werte",
                    "+",
                    "+---",
                    "+",
                    "+## 0e. Context Limit (Hardcoded + User-Präferenz)",
                    "+",
                    "+### 0d.1 Architektur-Entscheidung",
                    "+",
                    " **Gewählt: KISS - Hardcoded mit LocalStorage-Persistenz**",
                    " ",
                    " - Feste Werte: `[3, 5, 10, 15]`"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →