Erstellt: 2025-12-23 | Aktualisiert: 2025-12-27

Prozesse

Diese Kategorie dokumentiert die automatisierten Prozesse und Workflows, die bei der Arbeit mit Claude Code aktiv sind.

Inhalt

Übersicht

Das Hook-System ermöglicht:

MCP Server Refactoring Plan

Version: 1.2
Erstellt: 2025-12-28
Aktualisiert: 2025-12-28 (nach Supervision Runde 2)
Status: Final
Task: #507


1. Executive Summary

Ist-Zustand

Ziel

Konsolidierung zu einer modularen, wartbaren Architektur unter Einhaltung von:

Erwartete Verbesserungen


2. Analyse der Code-Duplikation

2.1 Kritische Duplikate (Sofort beheben)

db_connection.py (4 Dateien, ~240 LOC dupliziert)

ServerZeilenBibliothekBesonderheit
mcp-db59mysql.connector + PoolingDynamisches DB-Switching via USE
mcp-tasks62pymysqlFestes DB_NAME
mcp-contracts60pymysqlFestes DB_NAME
mcp-docs60pymysqlFestes DB_NAME

Problem: 3 von 4 Dateien sind nahezu identisch (mcp-tasks, mcp-contracts, mcp-docs).

Lösung: Zentralisieren in shared/infrastructure/simple_db_connection.py

WICHTIG: Diese Klasse heißt bewusst SimpleDbConnection, nicht DatabaseConnection. Sie bietet kein Pooling und ist nicht für High-Throughput geeignet. Für mcp-db (100+ Queries/Minute) bleibt die eigene Pooling-Implementierung.

# shared/infrastructure/simple_db_connection.py
"""
Einfache DB-Verbindung ohne Pooling.

NICHT geeignet für:
- High-Throughput (> 50 Queries/Minute)
- Connection Pooling Requirements
- mcp-db Server

Für diese Fälle: Eigene Implementierung mit mysql.connector.pooling
"""
from contextlib import contextmanager
from typing import Generator

import pymysql
from pymysql.connections import Connection

from shared.config_base import AppDatabaseConfig, LogDatabaseConfig


class SimpleDbConnection:
    """
    Einfache Datenbankverbindung ohne Pooling.
    
    Für Server mit geringem Query-Volumen (< 50/Minute).
    """
    
    @classmethod
    @contextmanager
    def get_connection(
        cls, 
        config: AppDatabaseConfig,
        database: str | None = None,
        autocommit: bool = False
    ) -> Generator[Connection, None, None]:
        """
        Context Manager für App-DB Connection.
        
        ACHTUNG: Keine weiteren Parameter hinzufügen!
        Bei Bedarf für mehr Logik: Neue Methode erstellen.
        
        Args:
            config: Server-Konfiguration mit DB-Credentials
            database: Optional - überschreibt config.DB_NAME
            autocommit: True für Logging, False für Transaktionen
        """
        conn = None
        db_name = database or getattr(config, 'DB_NAME', None)
        
        try:
            conn = pymysql.connect(
                host=config.DB_HOST,
                port=getattr(config, 'DB_PORT', 3306),
                user=config.DB_USER,
                password=config.DB_PASSWORD,
                database=db_name,
                charset="utf8mb4",
                cursorclass=pymysql.cursors.DictCursor,
                autocommit=autocommit,
            )
            yield conn
            if not autocommit:
                conn.commit()
        except Exception:
            if conn and not autocommit:
                conn.rollback()
            raise
        finally:
            if conn:
                conn.close()
    
    @classmethod
    @contextmanager
    def get_log_connection(
        cls, 
        config: LogDatabaseConfig
    ) -> Generator[Connection, None, None]:
        """Separate Verbindung für Logging (autocommit=True)."""
        conn = None
        try:
            conn = pymysql.connect(
                host=config.LOG_DB_HOST,
                user=config.LOG_DB_USER,
                password=config.LOG_DB_PASSWORD,
                database=config.LOG_DB_NAME,
                charset="utf8mb4",
                cursorclass=pymysql.cursors.DictCursor,
                autocommit=True,
            )
            yield conn
        finally:
            if conn:
                conn.close()

protokoll_logger.py (4 Dateien, ~240 LOC dupliziert)

Lösung: Zentralisieren mit strikter Clean Architecture Trennung

# shared/domain/log_entry.py
"""
Domain Entity für Log-Einträge.

REINE DOMAIN - keine Infrastructure-Abhängigkeiten!
"""
from dataclasses import dataclass
from datetime import datetime
from typing import Optional


@dataclass(frozen=True)
class LogEntry:
    """Standardisierter Log-Eintrag für alle MCP-Server."""
    timestamp: datetime
    client_name: str
    request: str
    status: str
    duration_ms: int
    error_message: Optional[str] = None
    tool_name: Optional[str] = None
    context_id: Optional[int] = None  # task_id, contract_id, etc.
# shared/infrastructure/protokoll_logger.py
"""
Infrastructure Logger für mcp_log Tabelle.

Importiert LogEntry aus Domain - definiert ihn NICHT selbst.
"""
import logging
import sys
from typing import TYPE_CHECKING

from shared.domain.log_entry import LogEntry  # Import aus Domain!
from shared.infrastructure.simple_db_connection import SimpleDbConnection

if TYPE_CHECKING:
    from shared.config_base import LogDatabaseConfig


class ProtokollLogger:
    """Fail-Safe Logger für mcp_log Tabelle."""
    
    def __init__(self, client_name: str, config: "LogDatabaseConfig"):
        self.client_name = client_name
        self.config = config
        self._logger = logging.getLogger(f"mcp.{client_name}")
    
    def log(self, entry: LogEntry) -> None:
        """Schreibt Log-Eintrag. Fehler gehen nur zu stderr."""
        try:
            with SimpleDbConnection.get_log_connection(self.config) as conn:
                with conn.cursor() as cursor:
                    request_str = self._format_request(entry)
                    cursor.execute(
                        """INSERT INTO mcp_log
                           (timestamp, client_name, request, status, duration_ms, error_message)
                           VALUES (%s, %s, %s, %s, %s, %s)""",
                        (
                            entry.timestamp,
                            self.client_name,
                            request_str[:500],
                            entry.status,
                            entry.duration_ms,
                            entry.error_message[:500] if entry.error_message else None,
                        )
                    )
        except Exception as e:
            print(f"CRITICAL: {self.client_name} log failed: {e}", file=sys.stderr)
    
    def _format_request(self, entry: LogEntry) -> str:
        parts = []
        if entry.tool_name:
            parts.append(f"[{entry.tool_name}]")
        if entry.context_id:
            parts.append(f"id={entry.context_id}")
        parts.append(entry.request[:400] if entry.request else "")
        return " ".join(parts)


# === Registry mit Test-Support ===

_logger_instances: dict[str, ProtokollLogger] = {}


def get_logger(client_name: str, config: "LogDatabaseConfig") -> ProtokollLogger:
    """Singleton-Factory für Logger."""
    if client_name not in _logger_instances:
        _logger_instances[client_name] = ProtokollLogger(client_name, config)
    return _logger_instances[client_name]


def clear_logger_registry() -> None:
    """
    Setzt die Logger-Registry zurück.
    
    NUR FÜR TESTS! Ermöglicht isolierte Test-Ausführung.
    """
    _logger_instances.clear()

config.py - Strikte Statische Konfiguration

# shared/config_base.py
"""
Basis-Konfiguration für alle MCP-Server.

WICHTIG: Diese Klasse wird NICHT instanziiert!
Alle Zugriffe erfolgen über Klassenattribute.

Falsch: config = BaseConfig()
Richtig: host = BaseConfig.DB_HOST
"""
import os
from typing import ClassVar, Protocol, runtime_checkable


@runtime_checkable
class AppDatabaseConfig(Protocol):
    """Protocol für App-Datenbank Konfiguration."""
    DB_HOST: str
    DB_PORT: int
    DB_USER: str
    DB_PASSWORD: str
    DB_NAME: str


@runtime_checkable
class LogDatabaseConfig(Protocol):
    """Protocol für Log-Datenbank Konfiguration."""
    LOG_DB_HOST: str
    LOG_DB_NAME: str
    LOG_DB_USER: str
    LOG_DB_PASSWORD: str


class BaseConfig:
    """
    Statische Konfiguration - NICHT INSTANZIIEREN!
    
    Verwendung:
        from shared.config_base import BaseConfig
        host = BaseConfig.DB_HOST  # Korrekt
        
        config = BaseConfig()  # FALSCH!
    """
    
    # App-Datenbank
    DB_HOST: ClassVar[str] = os.getenv("DB_HOST", "localhost")
    DB_PORT: ClassVar[int] = int(os.getenv("DB_PORT", "3306"))
    DB_USER: ClassVar[str] = os.getenv("DB_USER", "root")
    DB_PASSWORD: ClassVar[str] = os.getenv("DB_PASSWORD", "")
    DB_NAME: ClassVar[str] = os.getenv("DB_NAME", "ki_dev")
    
    # Log-Datenbank
    LOG_DB_HOST: ClassVar[str] = os.getenv("LOG_DB_HOST", "localhost")
    LOG_DB_NAME: ClassVar[str] = os.getenv("LOG_DB_NAME", "ki_dev")
    LOG_DB_USER: ClassVar[str] = os.getenv("LOG_DB_USER", "mcp_logger")
    LOG_DB_PASSWORD: ClassVar[str] = os.getenv("LOG_DB_PASSWORD", "")
    
    def __init__(self):
        raise TypeError("BaseConfig ist statisch und darf nicht instanziiert werden")

3. SRP-Verletzungen

3.1 ContractValidator (409 Zeilen) - Pragmatische Teilzerlegung

Empfohlenes Refactoring:

validators/
├── contract_validator.py      # Orchestrierung (< 150 Zeilen)
├── scope_resolver.py          # Pfad-Auflösung + Glob (< 100 Zeilen)
└── rule_evaluator.py          # Domain Service für Regelauswertung

HINWEIS: rule_evaluator.py ist ein Domain Service, kein Repository! Es führt keine CRUD-Operationen durch, sondern Business-Logik. Keine Repository-Nomenklatur für Validatoren verwenden.

Aktuelle Verantwortlichkeiten von rule_evaluator:

  1. Regel-Parsing
  2. Regel-Auswertung
  3. Regel-Aggregation

Status: Als Übergang akzeptabel, da reines In-Memory-Domain-Modul ohne IO.

Grenze: Sobald eine Regel externen Zustand berührt → weitere Zerlegung nötig.


4. Inkonsistenzen

4.1 Datenbank-Bibliotheken - Hybride Strategie

ServerBibliothekPoolingBegründung
mcp-dbmysql.connectorJa100+ Queries/Min, Performance-kritisch
anderepymysqlNein< 50 Queries/Min, Einfachheit > Performance

Trade-off explizit dokumentiert:

4.2 Zwei DB-Abstraktionen (konzedierte Redundanz)

ModulZweckPooling
shared/infrastructure/simple_db_connection.pyStandard-ServerNein
mcp-db/infrastructure/db_connection.pyHigh-ThroughputJa

Semantische Abgrenzung:


5. Ziel-Architektur

5.1 Verzeichnisstruktur (nach Package-Umbenennung)

/var/www/mcp-servers/
├── shared/                           # Gemeinsame Komponenten
│   ├── __init__.py
│   ├── config_base.py               # Statische BaseConfig + Protocols
│   ├── constants.py                 # ✓ Existiert bereits
│   ├── server_factory.py            # Server Creation Factory
│   ├── domain/
│   │   ├── __init__.py
│   │   └── log_entry.py             # LogEntry Dataclass (REINE DOMAIN)
│   └── infrastructure/
│       ├── __init__.py
│       ├── simple_db_connection.py  # Ohne Pooling, nicht für Hochlast
│       └── protokoll_logger.py      # Importiert LogEntry aus domain/
│
├── mcp_db/                          # Umbenannt von mcp-db!
│   ├── server.py
│   ├── config.py
│   ├── infrastructure/
│   │   └── db_connection.py         # EIGENE Version mit Pooling
│   └── tools/
│
├── mcp_tasks/                       # Umbenannt von mcp-tasks!
│   ├── server.py
│   ├── config.py
│   ├── domain/
│   ├── infrastructure/
│   │   └── task_repository.py
│   └── tools/
│
├── mcp_contracts/                   # Umbenannt!
│   ├── server.py
│   ├── config.py
│   ├── validators/
│   │   ├── contract_validator.py
│   │   ├── scope_resolver.py
│   │   └── rule_evaluator.py        # Domain Service, KEIN Repository!
│   └── tools/
│
├── mcp_docs/                        # Umbenannt!
│   └── ...
│
└── mcp_code/                        # Umbenannt!
    └── ...

5.2 Clean Architecture Schichten

┌─────────────────────────────────────────────────────────┐
│                    SHARED/DOMAIN                         │
│  ┌───────────────────────────────────────────────────┐  │
│  │ log_entry.py (reine Dataclass, keine Deps)       │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │ importiert
┌─────────────────────────────────────────────────────────┐
│                 SHARED/INFRASTRUCTURE                    │
│  ┌───────────────────────────────────────────────────┐  │
│  │ simple_db_connection.py, protokoll_logger.py     │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │ importiert
┌─────────────────────────────────────────────────────────┐
│                    SERVER-LAYER                          │
│  ┌─────────┐  ┌───────────┐  ┌─────────┐  ┌─────────┐  │
│  │ mcp_db  │  │ mcp_tasks │  │mcp_docs │  │mcp_code │  │
│  └─────────┘  └───────────┘  └─────────┘  └─────────┘  │
└─────────────────────────────────────────────────────────┘

6. Implementierungsplan mit Quality Gates

Phase 0: Package-Umbenennung (VORAUSSETZUNG)

Dauer: 0.5 Tage

SchrittAktionQuality Gate
0.1mcp-dbmcp_dbImport from mcp_db import ... funktioniert
0.2mcp-tasksmcp_tasksImport funktioniert
0.3mcp-contractsmcp_contractsImport funktioniert
0.4mcp-docsmcp_docsImport funktioniert
0.5mcp-codemcp_codeImport funktioniert
0.6systemd-Units aktualisierensystemctl status mcp-* = active
0.7Claude MCP Config aktualisierenAlle Tools erreichbar

Gate:

ACHTUNG: Phase 0 ist nicht optional! Ohne korrekte Package-Namen ist das gesamte Refactoring auf Sand gebaut.

Phase 1: Shared Foundation

Dauer: 2 Tage

SchrittAktionQuality Gate
1.1shared/config_base.py< 60 LOC, mypy clean
1.2shared/domain/log_entry.py< 25 LOC, frozen dataclass
1.3shared/infrastructure/simple_db_connection.py< 80 LOC
1.4shared/infrastructure/protokoll_logger.py< 70 LOC, importiert LogEntry
1.5shared/server_factory.py< 60 LOC
1.6Unit TestsDomain: 100% Coverage, Infra: 90-95%

Phase 1 Abnahmekriterien:

Phase 2: Server Migration

Phase 2a: mcp_docs (einfachstes)

Dauer: 0.5 Tage

Gate:

Phase 2b-2e: Weitere Server

(analog, siehe v1.1)

Universelle Regel für alle Server:

Phase 3: ContractValidator Refactoring

SchrittAktionQuality Gate
3.1scope_resolver.py< 100 LOC, 100% Coverage
3.2rule_evaluator.py (Domain Service)< 200 LOC, 100% Coverage
3.3contract_validator.py refactored< 150 LOC

7. Verbote und Grenzen

7.1 Absolute Verbote nach Phase 1

VerbotBegründung
sys.path.insert()Non-deterministisch, fragil
logging.basicConfig()Nicht deterministisch
lru_cache mit nicht-hashbaren ArgsTypeError zur Laufzeit
LogEntry Definition in InfrastructureClean Architecture Verletzung
Config InstanziierungStatisch-Only Design

7.2 Komplexitätsgrenzen

KonstruktGrenzeBei Überschreitung
get_connection() Parameter3Neue Methode erstellen
Datei-Länge300 LOCSplit erforderlich
Klassen-Verantwortlichkeiten3Weitere Zerlegung

8. Qualitätssicherung

Coverage-Strategie (differenziert)

LayerZiel-CoverageBegründung
Domain (shared/domain/)100%Reine Logik, keine Mocks nötig
Infrastructure90-95%DB-Code mit 100% führt zu Mock-Orgien
Server-Layer85-90%Integration, nicht Unit

Test-Isolation

# In jedem Test-Setup:
from shared.infrastructure.protokoll_logger import clear_logger_registry

def setup_function():
    clear_logger_registry()  # Isolierte Tests

9. Metriken

Vorher (Baseline)

MetrikWert
Dateien91
LOC9.248
Duplizierter Code~2.300 LOC (~25%)

Ziel Nachher (mit Phase-Gates)

PhaseMetrikZiel
Phase 0sys.path.insert Vorkommen0
Phase 1shared/ LOC< 350
Phase 1shared/domain Coverage100%
Phase 1shared/infrastructure Coverage>= 90%
Phase 2Gelöschte Duplikate>= 4 Dateien
GesamtLOC~6.500 (-30%)
GesamtDuplizierter Code< 800 LOC (< 12%)

10. Entscheidungen (Final)

Entscheidung 1: Package-Umbenennung

Entscheidung: Phase 0 (Voraussetzung, nicht optional)

Entscheidung 2: Datenbank-Bibliothek

Entscheidung: Hybrid mit klarer Semantik

Entscheidung 3: Singleton Pattern

Entscheidung: Dict-Registry mit clear_registry() für Tests

Entscheidung 4: Clean Architecture

Entscheidung: Strikte Trennung Domain/Infrastructure

Entscheidung 5: sys.path.insert

Entscheidung: Vollständig verboten nach Phase 0


Anhang A: Korrekturen nach Supervision

Runde 1 (v1.0 → v1.1)

ProblemKorrektur
.env Pfad falschExpliziter Parameter
lru_cache nicht hashbarDict-Registry
DB Config gemischtSeparate Protocols

Runde 2 (v1.1 → v1.2)

ProblemKorrektur
Package-Umbenennung unterschätztPhase 0 als Voraussetzung
LogEntry in InfrastructureStrikt in Domain
sys.path.insert als FallbackVollständig verboten
Logger Registry ohne Lifecycleclear_logger_registry()
100% Coverage auf InfraDifferenziert: Domain 100%, Infra 90-95%
SimpleDbConnection nicht benanntExpliziter Name, dokumentierte Grenzen
rule_engine als CRUDUmbenannt zu rule_evaluator (Domain Service)
Config instanziierbar__init__ wirft TypeError

Dokument Version 1.2 - Final für Implementierung.

Claude Pre-/Post-Hook System

Erstellt: 2025-12-23 | Aktualisiert: 2025-12-27

Das Hook-System greift automatisch bei jeder Interaktion mit Claude Code ein und stellt sicher, dass alle Aktionen protokolliert, validiert und abgesichert werden.

Operationale Übersicht: Claude Hooks (Betrieb)

Hook-Typen

Hook-Event Zeitpunkt Zweck
SessionStart Session-Beginn Protokollierung des Session-Starts
UserPromptSubmit Nach User-Eingabe Prompt-Logging
PreToolUse Vor Tool-Ausführung Validierung, Backup, Blockierung
PostToolUse Nach Tool-Ausführung Permissions, Response-Logging
Stop Task-Ende Abschluss-Protokollierung
SessionEnd Session-Ende Session-Abschluss

Konfiguration

Die Hooks werden in /root/.claude/settings.json konfiguriert.

Ablaufdiagramm: Edit/Write Operation

┌─────────────────┐
│  User-Prompt    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ UserPromptSubmit│──▶ log_to_db.py (Logging)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   PreToolUse    │
│   (Edit/Write)  │
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────┐ ┌───────────┐ ┌────────────┐
│Block- │ │file_backup│ │log_to_db.py│
│DB.py  │ │_hook.py   │ │(Logging)   │
└───┬───┘ └─────┬─────┘ └──────┬─────┘
    │           │              │
    │      Backup erstellt     │
    │           │              │
    └───────────┴──────────────┘
                │
         ┌──────┴──────┐
         │  Tool       │
         │  Execution  │
         └──────┬──────┘
                │
                ▼
┌─────────────────┐
│  PostToolUse    │
│  (Edit/Write)   │
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────────┐ ┌────────────┐
│fix-permi- │ │log_to_db.py│
│ssions.sh  │ │(Response)  │
└─────┬─────┘ └──────┬─────┘
      │              │
      ▼              │
  Permissions        │
  korrigiert         │
      └──────────────┘

Unterkapitel

Verwandte Themen

RAG-Prozess

Erstellt: 2025-12-24 | Aktualisiert: 2025-12-31

Status: Aktualisiert 2025-12-31

Übersicht

Der RAG-Prozess (Retrieval Augmented Generation) bildet das Herzstück der KI-gestützten Wissensgenerierung. Die Architektur trennt strikt zwischen Offline-Pipeline (Wissensaufbau) und Online-Pipeline (Wissenskonsum).

Grundprinzip


Offline-Pipeline: Tatsächlicher Ablauf (IST)

Pfad: /var/www/scripts/pipeline/
Orchestrator: pipeline.py
Step-Module: step_extract.py, step_load.py, step_transform.py, step_embed.py, step_semantic.py

Phase 0: Detection

StepModulBeschreibung
detectdetect.pyDateien scannen, Hash-Vergleich
queuedetect.pyNeue/geänderte Dateien in Queue

Phase 1: Extraction (pro Dokument)

StepModulBeschreibung
extractstep_extract.pyextract.pyText aus PDF/DOCX/PPTX/MD/TXT
hashstep_extract.pySHA256 Hash berechnen
rotationorientation.pySeitenrotation erkennen (PDF)

Phase 2: Load

StepModulBeschreibung
doc_createstep_load.pyDokument in documents anlegen
page_storestep_load.pySeiten in document_pages speichern

Phase 3: Vision (nur PDF)

StepModulBeschreibung
visionstep_transform.pyvision.pySeiten mit llama3.2-vision:11b analysieren
vision_storevision.pyErgebnisse in document_pages.vision_analysis

Phase 4: Chunking

StepModulBeschreibung
chunkstep_transform.pychunk.pyText in semantische Chunks zerlegen
chunk_storestep_load.pyChunks in chunks speichern

Phase 5: Enrichment (nur PDF)

StepModulBeschreibung
enrichstep_transform.pyenrich.pyVision-Kontext zu Chunks hinzufügen

Phase 6: Embedding (Layer 3 - Dokument wird suchbar)

StepModulBeschreibung
embedstep_embed.pyembed.pyEmbeddings mit mxbai-embed-large
qdrant_storeembed.pyVektoren in Qdrant speichern
status_embeddedstep_load.pyStatus = "embedded"

Nach Phase 6 ist das Dokument im Chat suchbar!

Phase 7: Semantic Analysis (Layer 4 - optional/async)

StepModulBeschreibung
entity_extractanalyzers/entity_extractor.pyEntitäten extrahieren (Anthropic/Ollama)
entity_storeanalyzers/entity_extractor.pyEntitäten in entities speichern
entity_normalizeanalyzers/entity_normalizer.pycanonical_name setzen
relation_extractanalyzers/relation_extractor.pyRelationen extrahieren
relation_storeanalyzers/document_analyzer.pyIn entity_relations speichern
taxonomy_classifyanalyzers/taxonomy_classifier.pyTaxonomie-Kategorien zuweisen
ontology_classifyanalyzers/ontology_classifier.pyOntologie-Klassen zuweisen
chunk_entity_linkanalyzers/document_analyzer.pyChunks mit Entitäten verknüpfen
chunk_taxonomyanalyzers/document_analyzer.pyTaxonomie auf Chunks propagieren
entity_taxonomyanalyzers/document_analyzer.pyTaxonomie auf Entitäten
chunk_semanticsanalyzers/semantic_analyzer.pyChunk-Semantik analysieren

Phase 8: Finalization

StepModulBeschreibung
status_donestep_load.pyStatus = "done"

Datenfluss-Diagramm

┌─────────────────────────────────────────────────────────────────┐
│ Phase 0: DETECTION                                               │
│ detect.py: scan_directory() → queue_files()                     │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 1: EXTRACTION                                              │
│ step_extract.py → extract.py                                    │
│   • Text aus PDF/DOCX/PPTX/MD/TXT                               │
│   • SHA256 Hash                                                 │
│   • Rotationserkennung (orientation.py)                         │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 2: LOAD                                                    │
│ step_load.py                                                    │
│   • documents Eintrag erstellen                                 │
│   • document_pages speichern                                    │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼ (nur PDF)
┌─────────────────────────────────────────────────────────────────┐
│ Phase 3: VISION                                                  │
│ step_transform.py → vision.py                                   │
│   • Seiten als Bilder rendern                                   │
│   • llama3.2-vision:11b Analyse                                 │
│   • Überschriften, Bilder, Tabellen, Layout erkennen            │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 4: CHUNKING                                                │
│ step_transform.py → chunk.py                                    │
│   • Semantisches Chunking nach Struktur                         │
│   • Überlappung: 10%                                            │
│   • Min: 100, Max: 2000 Zeichen                                 │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼ (nur PDF)
┌─────────────────────────────────────────────────────────────────┐
│ Phase 5: ENRICHMENT                                              │
│ step_transform.py → enrich.py                                   │
│   • Vision-Kontext zu Chunks hinzufügen                         │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 6: EMBEDDING (Layer 3)                                     │
│ step_embed.py → embed.py                                        │
│   • mxbai-embed-large (1024 dim)                                │
│   • Qdrant: Collection "documents"                              │
│   • Status: "embedded" ✓ SUCHBAR                                │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 7: SEMANTIC (Layer 4) - Optional                           │
│ step_semantic.py → analyzers/*                                  │
│   • Entity-Extraktion (Anthropic oder Ollama)                   │
│   • Relationen                                                  │
│   • Taxonomie-Klassifikation                                    │
│   • Ontologie-Zuordnung                                         │
│   • Chunk-Semantik (summary, keywords, sentiment)               │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 8: FINALIZATION                                            │
│   • Status: "done" ✓                                            │
└─────────────────────────────────────────────────────────────────┘

Konfiguration

Quelle: config.py

ParameterWertBeschreibung
NEXTCLOUD_PATH/var/www/nextcloud/data/root/files/DocumentsQuellverzeichnis
SUPPORTED_EXTENSIONS[".pdf", ".pptx", ".docx", ".md", ".txt"]Dateitypen
EMBEDDING_MODELmxbai-embed-largeOllama Embedding-Modell
EMBEDDING_DIMENSION1024Vektordimension
MIN_CHUNK_SIZE100Min. Chunk-Größe
MAX_CHUNK_SIZE2000Max. Chunk-Größe
CHUNK_OVERLAP_PERCENT10Überlappung
OCR_ENABLEDTrueOCR für Scans
OCR_LANGUAGEdeuOCR-Sprache
ROTATION_DETECTION_ENABLEDTrueSeitenrotation erkennen
ROTATION_OSD_CONFIDENCE_THRESHOLD2.0OSD Confidence-Schwelle
SEMANTIC_SYNCTrueSynchrone Semantik-Analyse
SEMANTIC_USE_ANTHROPICFalseAnthropic oder Ollama

Datei-Referenzen

Pipeline-Kern

/var/www/scripts/pipeline/
├── pipeline.py           # Orchestrator
├── config.py             # Konfiguration
├── db.py                 # Datenbank-Wrapper
├── detect.py             # Datei-Erkennung
├── extract.py            # Text-Extraktion
├── orientation.py        # Rotationserkennung (NEU!)
├── chunk.py              # Chunking
├── embed.py              # Embedding
├── vision.py             # Vision-Analyse
├── enrich.py             # Vision-Enrichment

Step-Module

├── step_extract.py       # Extraktions-Step
├── step_load.py          # Lade-Step
├── step_transform.py     # Transform-Step (Vision, Chunk, Enrich)
├── step_embed.py         # Embedding-Step
└── step_semantic.py      # Semantik-Step

Analyzer-Module

├── analyzers/
│   ├── document_analyzer.py     # Haupt-Orchestrator
│   ├── entity_extractor.py      # Entitäten
│   ├── entity_normalizer.py     # Normalisierung
│   ├── relation_extractor.py    # Relationen
│   ├── taxonomy_classifier.py   # Taxonomie
│   ├── ontology_classifier.py   # Ontologie
│   ├── semantic_analyzer.py     # Chunk-Semantik
│   └── statement_analyzer.py    # SPO-Aussagen

CLI-Befehle

# Vollständiger Durchlauf
python pipeline.py all

# Mit Tracking (für Web-UI)
python pipeline.py all --pipeline-id=4 --run-id=6

# Einzelne Phasen
python pipeline.py scan      # Nur scannen
python pipeline.py process   # Queue abarbeiten
python pipeline.py embed     # Ausstehende Embeddings
python pipeline.py semantic 123  # Semantik für Dokument 123
python pipeline.py status    # Status anzeigen

# Einzeldatei
python pipeline.py file /path/to/document.pdf

Datenbank-Tabellen

Content-Datenbank (ki_content)

TabelleZweck
documentsImportierte Dokumente
document_pagesEinzelne Seiten mit Vision-Analyse
chunksText-Chunks
entitiesExtrahierte Entitäten
entity_relationsRelationen zwischen Entitäten
chunk_entitiesChunk-Entity-Verknüpfungen
chunk_semanticsChunk-Analyse (summary, keywords)
taxonomy_termsTaxonomie-Begriffe
document_taxonomyDokument-Taxonomie-Zuordnung
entity_taxonomy_mappingEntity-Taxonomie-Zuordnung
ontology_classesOntologie-Klassen
pipeline_configsPipeline-Konfigurationen
pipeline_stepsPipeline-Schritte
pipeline_runsPipeline-Ausführungen
pipeline_queuePipeline-Warteschlange
semantic_queueSemantik-Warteschlange

Dev-Datenbank (ki_dev)

TabelleZweck
pipeline_logPipeline-Logs

Referenz

Verwandte Themen

Quality Gates

Erstellt: 2025-12-23 | Aktualisiert: 2025-12-28

Automatische Code-Qualitätsprüfung via Claude Code Hooks mit Task-Integration.

StatusAktiv
Implementiert2025-12-23 (Phase 1+2+3: 2025-12-28)
Regeln31 BLOCK + 50 WARN = 81 total
Task-IntegrationAutomatisch bei Violations

Architektur

/var/www/tools/ki-protokoll/claude-hook/
├── hook_dispatcher.py      ← Stabiler Einstiegspunkt
├── .env                    ← DB-Credentials
└── quality/
    ├── pre_rules.py            ← BLOCK-Regeln Dispatcher
    ├── post_rules.py           ← WARN-Regeln Dispatcher
    ├── pre_rules_guard.py      ← P1.x SRP, P4.x OOP
    ├── pre_rules_mvc.py        ← P2.x MVC
    ├── pre_rules_validation.py ← P3.x PSR
    ├── pre_rules_layers.py     ← P6.x SOLID
    ├── pre_rules_htmx.py       ← HTMX-C1-C5
    ├── pre_rules_constants.py  ← P7.x Magic Numbers
    ├── pre_rules_deterministic.py ← P8.x Deterministic
    ├── pre_rules_tests.py      ← P14.x Test Isolation
    ├── pre_rules_python.py     ← PP1.x Python
    ├── rules_quality.py        ← W1.x-W6.x Quality
    ├── rules_security.py       ← Security Warnings
    ├── rules_style.py          ← Style Warnings
    ├── rules_constants.py      ← W7.x Magic Numbers
    ├── rules_failfast.py       ← W8.x Fail Fast
    ├── rules_failsafe.py       ← W9.x Fail Safe (Phase 3)
    ├── rules_tradeoffs.py      ← W10.x Trade-offs (Phase 3)
    ├── rules_leastsurprise.py  ← W15.x Least Surprise
    ├── rules_testisolation.py  ← W14.x Test Isolation
    ├── phpmetrics_check.py     ← W13.x Cohesion (Phase 3)
    └── task_creator.py         ← Violations → Tasks

Prüfungen (15 Prinzipien)

#PrüfungPre-Hook (BLOCK)Post-Hook (WARN)
1SRP + KISSP1.1 Header, P1.2 MüllhaldenW1.1-W1.5 Metriken
2MVC + CRUDP2.1-P2.4 SQL, Trans, echoW2.1-W2.2 Keywords
3PSR + TypesP3.1-P3.4 strict, NS, ReturnW3.1, W3.3 Params
4OOPP4.1 Public PropertyW4.1-W4.4 Anämie
5DRY-W5.1-W5.2 Duplikate
6SOLID + DIPP6.1-P6.2 Layer-ImportsW6.1-W6.2 Interface
7ConstantsP7.1-P7.3 Magic NumbersW7.1-W7.5 Magic Strings
8DeterministicP8.1-P8.9 time, rand, globalW8.1-W8.5 Fail Fast
9Fail Safe-W9.1-W9.6 try-finally, cleanup
10Trade-offs-W10.1-W10.6 ADR, Factory, Cache
13Cohesion-W13.1-W13.6 LCOM, Coupling
14Test IsolationP14.1-P14.6 Prod-DB, TRUNCATEW14.1-W14.7 tearDown, sleep
15Least Surprise-W15.1-W15.6 Getter Side Effects

Fett = Neu in Phase 3 (2025-12-28)

Phase 3: Neue Regeln

W9.x Fail Safe (WARN)

  • W9.1: try-Block mit Ressourcen ohne finally
  • W9.2: DB-Operationen ohne Exception-Handling
  • W9.3: catch ohne Logging oder Rethrow
  • W9.4: Ressourcen-Properties ohne __destruct
  • W9.5: exit/die ohne Shutdown-Handler
  • W9.6: Connection ohne passenden Close

W10.x Trade-off Documentation (WARN)

  • W10.1: Factory Pattern ohne ADR-Referenz
  • W10.2: Event-Architektur ohne Dokumentation
  • W10.3: Caching ohne Invalidation-Strategie
  • W10.4: 3+ Traits ohne Kompositions-Doku
  • W10.5: External Service ohne Fehlerbehandlungs-Doku
  • W10.6: @deprecated ohne Migrations-Hinweis

W13.x Cohesion (via PHPMetrics)

  • W13.1: LCOM > 4 (Low Cohesion)
  • W13.2: Afferent Coupling > 10
  • W13.3: Efferent Coupling > 15
  • W13.4: Sehr stabile Klasse mit vielen Dependents
  • W13.5: Methoden-Komplexität CCN > 15
  • W13.6: WMC > 50 (Class too complex)

PHPMetrics Integration

# Installation
/opt/php-tools/vendor/bin/phpmetrics

# Analyse ausführen
/opt/php-tools/vendor/bin/phpmetrics --report-json=/tmp/phpmetrics.json /var/www/dev.campus.systemische-tools.de/src

# Cohesion-Check (Hook-Integration)
python3 /var/www/tools/ki-protokoll/claude-hook/quality/phpmetrics_check.py

ADR Template

Für Trade-off Dokumentation: /var/www/dev.campus.systemische-tools.de/docs/templates/adr-template.md

Prinzipien-Abdeckung

KategorieAnzahlAnteil
✅ Gut abgedeckt (≥70%)12/1580%
⚠️ Teilweise (30-69%)2/1513%
❌ Nicht automatisierbar1/157%

Detaillierte Dokumentation

Chat UI Redesign: 3-Spalten-Layout

Erstellt: 2025-12-27 | Aktualisiert: 2025-12-27

Task #424 - KORRIGIERTER Implementierungsplan
Version: 2.0 (nach kritischem Review)
Datum: 2025-12-27


Architektur-Entscheidungen

Wiederverwendung bestehender Infrastruktur

KomponenteStatusAktion
ManageConfigUseCaseExistiertWiederverwenden
ContentConfigRepositoryExistiertWiederverwenden
ConfigController (Web)ExistiertNicht ändern
Controller\Api\ConfigControllerFehltNEU erstellen

Korrigierte Prinzipien

PrinzipAlter PlanKorrektur
SRPChatController erweiternNeuer Api\ConfigController
DRYDoppelte DropdownsSettings-Bereich entfernen
MVCVersion-Logik im ControllerUseCase nutzen
ISPInterface unvollständigInterface erweitern
YAGNIHistory-BrowserNur Save = neue Version
WCAGFehlende ARIAVollständige A11y

Dateien und Änderungen

1. NEUER Controller: src/Controller/Api/ConfigController.php

<?php

declare(strict_types=1);

namespace Controller\Api;

// @responsibility: JSON-API für Config-Operationen (Chat-Integration)

use Framework\Controller;
use UseCases\Config\ManageConfigUseCase;

class ConfigController extends Controller
{
    public function __construct(
        private ManageConfigUseCase $configUseCase
    ) {
    }

    /**
     * GET /api/v1/config/{id}
     * Lädt Config mit Content für Anzeige
     */
    public function show(string $id): void
    {
        $config = $this->configUseCase->getById((int) $id);
        
        if ($config === null) {
            $this->json(['error' => 'Config nicht gefunden'], 404);
            return;
        }

        $this->json([
            'id' => $config->id,
            'name' => $config->name,
            'type' => $config->type,
            'content' => $config->content,
            'version' => $config->version,
        ]);
    }

    /**
     * POST /api/v1/config/{id}
     * Speichert neue Version (auto-increment)
     */
    public function update(string $id): void
    {
        $this->requireCsrf();

        $config = $this->configUseCase->getById((int) $id);
        if ($config === null) {
            $this->json(['error' => 'Config nicht gefunden'], 404);
            return;
        }

        $newContent = $_POST['content'] ?? '';
        
        // Auto-increment Version
        $currentVersion = $config->version ?? '1.0';
        $versionParts = explode('.', $currentVersion);
        $newVersion = $versionParts[0] . '.' . ((int) ($versionParts[1] ?? 0) + 1);

        $result = $this->configUseCase->update(
            id: (int) $id,
            name: $config->name,
            slug: $config->slug,
            description: $config->description,
            content: $newContent,
            newVersion: $newVersion,
            changeDescription: 'Chat UI Update',
            status: $config->status,
            parentId: $config->parentId
        );

        if (!$result->success) {
            $this->json(['error' => $result->message], 400);
            return;
        }

        $this->json([
            'success' => true,
            'version' => $newVersion,
            'message' => "Version {$newVersion} gespeichert",
        ]);
    }
}

Zeilen: ~70 | Dependencies: 1 (ManageConfigUseCase) | SRP: Nur Config-API


2. Routes: routes/api.php

Hinzufügen:

// Config API (für Chat-Integration)
use Controller\Api\ConfigController;

$router->get('/api/v1/config/{id}', [ConfigController::class, 'show']);
$router->post('/api/v1/config/{id}', [ConfigController::class, 'update']);

3. Interface erweitern: src/Domain/Repository/ContentConfigRepositoryInterface.php

interface ContentConfigRepositoryInterface
{
    // ... bestehende Methoden ...

    /**
     * Save version to history before update.
     */
    public function saveHistory(
        int $configId,
        string $content,
        string $version,
        string $changedBy,
        string $changeDescription
    ): void;

    /**
     * Get version history for a config.
     *
     * @return array<int, array<string, mixed>>
     */
    public function getHistory(int $configId, int $limit = 10): array;
}

4. View: src/View/chat/index.php

ENTFERNEN aus .chat-settings--advanced:

NEU einfügen nach <aside class="chat-sidebar">...</aside>:

<!-- Config Panel -->
<aside class="chat-config" id="configPanel">
    <div class="chat-config__header">
        <span>Konfiguration</span>
    </div>

    <!-- System Prompt -->
    <div class="chat-config__section" data-config-type="system_prompt">
        <label for="configSystemPrompt" class="chat-config__label">System Prompt</label>
        <div class="chat-config__row">
            <select name="system_prompt_id" id="configSystemPrompt" class="chat-config__select">
                <?php foreach ($systemPrompts ?? [] as $prompt): ?>
                <option value="<?= $prompt['id'] ?>"
                        data-content="<?= htmlspecialchars($prompt['content'] ?? '{}') ?>">
                    <?= htmlspecialchars($prompt['name']) ?>
                </option>
                <?php endforeach; ?>
            </select>
            <button type="button" 
                    class="chat-config__toggle"
                    aria-expanded="false"
                    aria-controls="systemPromptEdit"
                    aria-label="System Prompt bearbeiten">
                <span aria-hidden="true">▸</span>
            </button>
        </div>
        <div class="chat-config__edit" id="systemPromptEdit" hidden>
            <textarea id="systemPromptText" class="chat-config__textarea" rows="8"></textarea>
            <button type="button" class="chat-config__save" data-config-id="">
                Speichern
            </button>
        </div>
    </div>
    <!-- ... weitere Sections (Struktur, Autor-Profil) ... -->
</aside>

5. CSS: public/css/chat-redesign.css

/* CONFIG PANEL (3-Spalten-Layout) */

.chat-config {
    width: 300px;
    background: var(--chat-sidebar-bg);
    border-right: 1px solid var(--chat-border);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    overflow-y: auto;
}

.chat-config__header {
    padding: 16px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--chat-text-secondary);
    border-bottom: 1px solid var(--chat-border);
}

.chat-config__section { padding: 12px 16px; }
.chat-config__label { display: block; font-size: 11px; margin-bottom: 6px; }
.chat-config__row { display: flex; gap: 8px; align-items: center; }
.chat-config__select { flex: 1; padding: 8px 10px; border-radius: 6px; }
.chat-config__toggle { padding: 6px 10px; border-radius: 6px; cursor: pointer; }
.chat-config__textarea { width: 100%; font-family: monospace; font-size: 12px; }
.chat-config__save { width: 100%; padding: 8px; background: var(--chat-accent); }

@media (max-width: 1024px) {
    .chat-config { display: none; }
}

Zusammenfassung der Änderungen

DateiAktionZeilen
src/Controller/Api/ConfigController.phpNEU~70
routes/api.phpERWEITERN+3
src/Domain/Repository/ContentConfigRepositoryInterface.phpERWEITERN+15
src/View/chat/index.phpÄNDERN~100
public/css/chat-redesign.cssERWEITERN~120

Total neue Zeilen: ~310 | Geänderte Dateien: 5 | Neue Dateien: 1


Checkliste vor Implementation


Nicht implementiert (YAGNI)

Verwandte Themen

db.py Refactoring Plan

Status

Ausgangslage

Datei: /var/www/scripts/pipeline/db.py
Zeilen: 834
Klassen: 2 (Database: 707 Zeilen, PipelineProgress: 109 Zeilen)
Problem: 14 verschiedene Verantwortlichkeiten in der Database-Klasse (SRP-Verletzung)

Ziele

  1. Jedes Modul unter 500 Zeilen (Code Hygiene Limit)
  2. Strikte Einhaltung von SRP (Single Responsibility Principle)
  3. 100% Rückwärtskompatibilität - alle bestehenden Imports funktionieren weiterhin
  4. DRY, KISS, SOLID Prinzipien

Architektur: Mixin-Pattern

Python Mixins ermöglichen das Aufteilen einer Klasse in logische Einheiten bei gleichzeitiger Beibehaltung der Abwärtskompatibilität.

db_core.py          ─┐
db_documents.py      │
db_queue.py          ├── Mixins ──► db.py (Database erbt von allen)
db_logging.py        │
db_semantic.py       │
db_prompts.py       ─┘

Modul-Aufteilung

1. db_core.py (~100 Zeilen)

Verantwortung: Connection Management

class DatabaseCore:
    def __init__(self): ...
    def connect(self): ...
    def disconnect(self): ...
    def execute(self, query, params=None): ...
    def commit(self): ...

2. db_documents.py (~150 Zeilen)

Verantwortung: Document, Page, Chunk CRUD

class DocumentsMixin:
    # Documents
    def document_exists(self, file_path): ...
    def document_is_done(self, file_path): ...
    def insert_document(self, file_path, title, file_type, file_size, file_hash): ...
    def update_document_status(self, doc_id, status, error_message=None): ...
    
    # Pages
    def insert_page(self, doc_id, page_number, text_content, token_count=None): ...
    def get_page_id(self, doc_id, page_number): ...
    
    # Chunks
    def insert_chunk(self, doc_id, chunk_index, content, heading_path, ...): ...
    def get_chunks_for_embedding(self, limit=DEFAULT_LIMIT): ...
    def update_chunk_qdrant_id(self, chunk_id, qdrant_id): ...

3. db_queue.py (~60 Zeilen)

Verantwortung: Pipeline Queue Operations

class QueueMixin:
    def add_to_queue(self, file_path, action="process"): ...
    def get_pending_queue_items(self, limit=10): ...
    def update_queue_status(self, queue_id, status, error_message=None): ...

4. db_logging.py (~180 Zeilen)

Verantwortung: Alle Logging-Operationen

class LoggingMixin:
    def log(self, level, message, context=None): ...
    def log_to_protokoll(self, client_name, request, response=None, ...): ...
    def log_provenance(self, artifact_type, artifact_id, source_type, ...): ...

5. db_semantic.py (~250 Zeilen)

Verantwortung: Entity Types, Stopwords, Taxonomy, Synonyms

class SemanticMixin:
    # Entity Types
    def get_entity_types(self, active_only=True): ...
    def get_entity_type_codes(self): ...
    def build_entity_prompt_categories(self): ...
    
    # Stopwords
    def get_stopwords(self, active_only=True): ...
    def is_stopword(self, word): ...
    def _normalize_stopword(self, word): ...
    
    # Synonyms (internal, nicht extern genutzt)
    def find_entity_by_synonym(self, synonym): ...
    def add_synonym(self, entity_id, synonym, ...): ...
    
    # Chunk Taxonomy
    def add_chunk_taxonomy(self, chunk_id, term_id, ...): ...
    def get_chunk_taxonomies(self, chunk_id): ...
    
    # Entity Taxonomy
    def add_entity_taxonomy(self, entity_id, term_id, ...): ...
    def get_entity_taxonomies(self, entity_id): ...
    def get_taxonomy_terms(self): ...

6. db_prompts.py (~70 Zeilen)

Verantwortung: Prompt-Verwaltung

class PromptsMixin:
    def get_prompt(self, name, version=None): ...
    def get_prompt_by_use_case(self, use_case, version=None): ...

7. db.py (~100 Zeilen - Kompositions-Layer)

Verantwortung: Rückwärtskompatibilität

from db_core import DatabaseCore
from db_documents import DocumentsMixin
from db_queue import QueueMixin
from db_logging import LoggingMixin
from db_semantic import SemanticMixin
from db_prompts import PromptsMixin

class Database(
    DatabaseCore,
    DocumentsMixin,
    QueueMixin,
    LoggingMixin,
    SemanticMixin,
    PromptsMixin
):
    '''Vollständige Database-Klasse mit allen Operationen.'''
    pass

class PipelineProgress:
    # ... (unverändert, eigene Klasse)

# Globale Instanz für Rückwärtskompatibilität
db = Database()

Zeilen-Verteilung

ModulZeilenVerantwortlichkeiten
db_core.py~100Connection
db_documents.py~150Documents, Pages, Chunks
db_queue.py~60Queue
db_logging.py~180Log, Protokoll, Provenance
db_semantic.py~250Types, Stopwords, Taxonomy
db_prompts.py~70Prompts
db.py~100Komposition + PipelineProgress
Total~910(verteilt auf 7 Dateien)

Rückwärtskompatibilität

Alle existierenden Imports funktionieren weiterhin:

# Diese Imports bleiben gültig:
from db import db
from db import Database
from db import PipelineProgress
from db import db, PipelineProgress

Implementierungs-Reihenfolge

  1. db_core.py erstellen (Basis für alle anderen)
  2. db_logging.py erstellen (wird von vielen benötigt)
  3. db_prompts.py erstellen (eigenständig)
  4. db_documents.py erstellen (eigenständig)
  5. db_queue.py erstellen (eigenständig)
  6. db_semantic.py erstellen (eigenständig)
  7. db.py umschreiben (Komposition)
  8. Tests durchführen (alle Pipeline-Scripts testen)

Risiken & Mitigationen

RisikoMitigation
Import-Fehler nach RefactoringKompositions-Layer in db.py exportiert alles
Circular ImportsJedes Modul importiert nur db_core.py
Connection-SharingMixins nutzen self.connection von DatabaseCore
Tests fehlschlagenSchrittweise Tests nach jedem Modul

Cleanup-Möglichkeiten

Erstellt: 2025-12-22 | Aktualisiert: 2025-12-27

Refactoring-Session 22.12.2025 - Abgeschlossen

Zusammenfassung

Umfangreiche Architektur-Verbesserungen basierend auf externer Supervision. Alle 9 Phasen erfolgreich implementiert.

Abgeschlossene Phasen

PhaseBeschreibungStatus
1.1DatabaseFactory als einzige DB-QuelleOK
1.2Repository-Pattern konsequent umgesetztOK
2.1Application Services eingefuehrtOK
2.2Command/Query-ObjekteOK
2.3DI-Container fuer ControllerOK
3.1Response-Pipeline vereinheitlichtOK
3.2Formatter/Renderer-KlassenOK
4.1State Machines fuer Domain-ObjekteOK
4.2Audit-Logging systematisiertOK

Neue Komponenten (Phase 3-4)

Formatter (src/Infrastructure/Formatter/)

State Machines (src/Domain/ValueObject/)

Logging (src/Infrastructure/Logging/)

Middleware (src/Framework/Middleware/)

Exception (src/Domain/Exception/)

Datenbank

Validierung Phase 6

Contract-Validierung

Controller LOC-Pruefung

15 von 17 Controllern unter 300 LOC. ContentController (397) und ContentPipelineController (631) noch zu gross.

PDO-Pruefung

Keine direkten PDO-Zugriffe in Controllern - OK

PHP-Quality-Check

Bekannte Probleme (vorbestehend)

Architektur nach Refactoring

Request
   |
   v
CorrelationMiddleware (X-Correlation-ID)
   |
   v
Router -> Controller
              |
              v
         Application Service
              |
              +-> Repository (DB via DatabaseFactory)
              |
              +-> Domain ValueObjects (State Machines)
              |
              +-> AuditLogger (Logging)
              |
              v
         Formatter (API/Date)
              |
              v
         Response (View/JSON/Redirect)