{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_tradeoffs.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Trade-off Documentation Regeln (WARN).\n\nW10.x Regeln: Warnt bei Architektur-Mustern ohne Trade-off-Dokumentation.\n\nPrinzip: \"Make Trade-offs Explicit\" (#10)\n\nDiese Regeln erkennen komplexe Architektur-Entscheidungen und\nwarnen, wenn keine entsprechende Dokumentation vorhanden ist.\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule\n\n\n# =============================================================================\n# W10: TRADE-OFF DOCUMENTATION\n# =============================================================================\n\nclass W10_1_AbstractFactoryWithoutDocs(Rule):\n \"\"\"W10.1: Abstract Factory Pattern ohne @see ADR-Verweis.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Erkennung: Abstract class mit Factory im Namen und create-Methoden\n is_factory = bool(re.search(\n r\"abstract\\s+class\\s+\\w*Factory\",\n content\n )) or bool(re.search(\n r\"interface\\s+\\w*Factory\",\n content\n ))\n\n if is_factory:\n # Prüfe ob ADR-Referenz vorhanden\n has_adr_ref = bool(re.search(\n r\"@see\\s+ADR-\\d+|ADR-\\d+|Architecture Decision\",\n content,\n re.IGNORECASE\n ))\n\n if not has_adr_ref:\n warnings.append(\n \"W10.1: Factory pattern without architecture documentation. \"\n \"Add @see ADR-XXX reference explaining the design choice.\"\n )\n\n return warnings\n\n\nclass W10_2_EventDispatcherWithoutDocs(Rule):\n \"\"\"W10.2: Event-basierte Architektur ohne Dokumentation.\"\"\"\n\n EVENT_PATTERNS = [\n r\"class\\s+\\w+Event\\b\",\n r\"implements\\s+\\w*EventSubscriber\",\n r\"->dispatch\\s*\\(\\s*new\\s+\\w+Event\",\n r\"EventDispatcher\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Framework\/\", # Framework definiert Event-System\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_events = any(\n re.search(pattern, content)\n for pattern in self.EVENT_PATTERNS\n )\n\n if has_events:\n has_docs = bool(re.search(\n r\"@see\\s+ADR|Event\\s+Flow|Event-Dokumentation\",\n content,\n re.IGNORECASE\n ))\n\n if not has_docs:\n warnings.append(\n \"W10.2: Event-based architecture without documentation. \"\n \"Consider documenting event flow with ADR.\"\n )\n\n return warnings\n\n\nclass W10_3_CacheWithoutStrategy(Rule):\n \"\"\"W10.3: Caching ohne dokumentierte Invalidation-Strategie.\"\"\"\n\n CACHE_PATTERNS = [\n r\"->cache\\s*\\(\",\n r\"Cache::\",\n r\"setCache\\s*\\(\",\n r\"apcu_\",\n r\"redis\",\n r\"memcache\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Infrastructure\/Cache\/\", # Cache-Infrastruktur selbst\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_cache = any(\n re.search(pattern, content, re.IGNORECASE)\n for pattern in self.CACHE_PATTERNS\n )\n\n if has_cache:\n has_invalidation_docs = bool(re.search(\n r\"invalidat|TTL|cache\\s+strateg|@cache\",\n content,\n re.IGNORECASE\n ))\n\n if not has_invalidation_docs:\n warnings.append(\n \"W10.3: Caching without invalidation strategy. \"\n \"Document cache TTL and invalidation approach.\"\n )\n\n return warnings\n\n\nclass W10_4_MultipleInheritanceWithoutDocs(Rule):\n \"\"\"W10.4: Trait-Nutzung ohne Dokumentation der Komposition.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Zähle verwendete Traits\n trait_uses = re.findall(r\"use\\s+(\\w+Trait)\", content)\n\n if len(trait_uses) >= 3:\n has_composition_docs = bool(re.search(\n r\"@composition|@uses|Trait-Komposition|Mixin\",\n content,\n re.IGNORECASE\n ))\n\n if not has_composition_docs:\n warnings.append(\n f\"W10.4: Class uses {len(trait_uses)} traits without documentation. \"\n \"Document trait composition and potential conflicts.\"\n )\n\n return warnings\n\n\nclass W10_5_ExternalServiceWithoutDocs(Rule):\n \"\"\"W10.5: External Service Integration ohne Fehlerbehandlungs-Doku.\"\"\"\n\n EXTERNAL_PATTERNS = [\n r\"curl_\",\n r\"file_get_contents\\s*\\(\\s*['\\\"]https?:\",\n r\"new\\s+GuzzleHttp\",\n r\"new\\s+HttpClient\",\n r\"->request\\s*\\(\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Infrastructure\/External\/\",\n \"\/Infrastructure\/Http\/\",\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_external = any(\n re.search(pattern, content)\n for pattern in self.EXTERNAL_PATTERNS\n )\n\n if has_external:\n has_error_handling_docs = bool(re.search(\n r\"@throws|timeout|retry|fallback|circuit.?breaker\",\n content,\n re.IGNORECASE\n ))\n\n if not has_error_handling_docs:\n warnings.append(\n \"W10.5: External service call without error handling documentation. \"\n \"Document timeout, retry, and fallback strategy.\"\n )\n\n return warnings\n\n\nclass W10_6_DeprecationWithoutMigration(Rule):\n \"\"\"W10.6: @deprecated ohne Migrations-Hinweis.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Finde @deprecated Annotations\n deprecated = re.findall(\n r\"@deprecated\\s+(.{0,100})\",\n content,\n re.IGNORECASE\n )\n\n for deprecation_msg in deprecated:\n # Prüfe ob Migration dokumentiert ist\n has_migration = bool(re.search(\n r\"use\\s+\\w+\\s+instead|migrate\\s+to|replacement|@see\",\n deprecation_msg,\n re.IGNORECASE\n ))\n\n if not has_migration:\n warnings.append(\n \"W10.6: @deprecated without migration path. \"\n \"Add 'Use XY instead' or '@see NewClass'.\"\n )\n break\n\n return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W10_1_AbstractFactoryWithoutDocs(),\n W10_2_EventDispatcherWithoutDocs(),\n W10_3_CacheWithoutStrategy(),\n W10_4_MultipleInheritanceWithoutDocs(),\n W10_5_ExternalServiceWithoutDocs(),\n W10_6_DeprecationWithoutMigration(),\n]\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_tradeoffs.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Trade-off Documentation Regeln (WARN).\n\nW10.x Regeln: Warnt bei Architektur-Mustern ohne Trade-off-Dokumentation.\n\nPrinzip: \"Make Trade-offs Explicit\" (#10)\n\nDiese Regeln erkennen komplexe Architektur-Entscheidungen und\nwarnen, wenn keine entsprechende Dokumentation vorhanden ist.\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule\n\n\n# =============================================================================\n# W10: TRADE-OFF DOCUMENTATION\n# =============================================================================\n\nclass W10_1_AbstractFactoryWithoutDocs(Rule):\n \"\"\"W10.1: Abstract Factory Pattern ohne @see ADR-Verweis.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Erkennung: Abstract class mit Factory im Namen und create-Methoden\n is_factory = bool(re.search(\n r\"abstract\\s+class\\s+\\w*Factory\",\n content\n )) or bool(re.search(\n r\"interface\\s+\\w*Factory\",\n content\n ))\n\n if is_factory:\n # Prüfe ob ADR-Referenz vorhanden\n has_adr_ref = bool(re.search(\n r\"@see\\s+ADR-\\d+|ADR-\\d+|Architecture Decision\",\n content,\n re.IGNORECASE\n ))\n\n if not has_adr_ref:\n warnings.append(\n \"W10.1: Factory pattern without architecture documentation. \"\n \"Add @see ADR-XXX reference explaining the design choice.\"\n )\n\n return warnings\n\n\nclass W10_2_EventDispatcherWithoutDocs(Rule):\n \"\"\"W10.2: Event-basierte Architektur ohne Dokumentation.\"\"\"\n\n EVENT_PATTERNS = [\n r\"class\\s+\\w+Event\\b\",\n r\"implements\\s+\\w*EventSubscriber\",\n r\"->dispatch\\s*\\(\\s*new\\s+\\w+Event\",\n r\"EventDispatcher\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Framework\/\", # Framework definiert Event-System\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_events = any(\n re.search(pattern, content)\n for pattern in self.EVENT_PATTERNS\n )\n\n if has_events:\n has_docs = bool(re.search(\n r\"@see\\s+ADR|Event\\s+Flow|Event-Dokumentation\",\n content,\n re.IGNORECASE\n ))\n\n if not has_docs:\n warnings.append(\n \"W10.2: Event-based architecture without documentation. \"\n \"Consider documenting event flow with ADR.\"\n )\n\n return warnings\n\n\nclass W10_3_CacheWithoutStrategy(Rule):\n \"\"\"W10.3: Caching ohne dokumentierte Invalidation-Strategie.\"\"\"\n\n CACHE_PATTERNS = [\n r\"->cache\\s*\\(\",\n r\"Cache::\",\n r\"setCache\\s*\\(\",\n r\"apcu_\",\n r\"redis\",\n r\"memcache\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Infrastructure\/Cache\/\", # Cache-Infrastruktur selbst\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_cache = any(\n re.search(pattern, content, re.IGNORECASE)\n for pattern in self.CACHE_PATTERNS\n )\n\n if has_cache:\n has_invalidation_docs = bool(re.search(\n r\"invalidat|TTL|cache\\s+strateg|@cache\",\n content,\n re.IGNORECASE\n ))\n\n if not has_invalidation_docs:\n warnings.append(\n \"W10.3: Caching without invalidation strategy. \"\n \"Document cache TTL and invalidation approach.\"\n )\n\n return warnings\n\n\nclass W10_4_MultipleInheritanceWithoutDocs(Rule):\n \"\"\"W10.4: Trait-Nutzung ohne Dokumentation der Komposition.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Zähle verwendete Traits\n trait_uses = re.findall(r\"use\\s+(\\w+Trait)\", content)\n\n if len(trait_uses) >= 3:\n has_composition_docs = bool(re.search(\n r\"@composition|@uses|Trait-Komposition|Mixin\",\n content,\n re.IGNORECASE\n ))\n\n if not has_composition_docs:\n warnings.append(\n f\"W10.4: Class uses {len(trait_uses)} traits without documentation. \"\n \"Document trait composition and potential conflicts.\"\n )\n\n return warnings\n\n\nclass W10_5_ExternalServiceWithoutDocs(Rule):\n \"\"\"W10.5: External Service Integration ohne Fehlerbehandlungs-Doku.\"\"\"\n\n EXTERNAL_PATTERNS = [\n r\"curl_\",\n r\"file_get_contents\\s*\\(\\s*['\\\"]https?:\",\n r\"new\\s+GuzzleHttp\",\n r\"new\\s+HttpClient\",\n r\"->request\\s*\\(\",\n ]\n\n def __init__(self):\n super().__init__(allowlist=[\n \"\/Infrastructure\/External\/\",\n \"\/Infrastructure\/Http\/\",\n ])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n has_external = any(\n re.search(pattern, content)\n for pattern in self.EXTERNAL_PATTERNS\n )\n\n if has_external:\n has_error_handling_docs = bool(re.search(\n r\"@throws|timeout|retry|fallback|circuit.?breaker\",\n content,\n re.IGNORECASE\n ))\n\n if not has_error_handling_docs:\n warnings.append(\n \"W10.5: External service call without error handling documentation. \"\n \"Document timeout, retry, and fallback strategy.\"\n )\n\n return warnings\n\n\nclass W10_6_DeprecationWithoutMigration(Rule):\n \"\"\"W10.6: @deprecated ohne Migrations-Hinweis.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=[])\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Finde @deprecated Annotations\n deprecated = re.findall(\n r\"@deprecated\\s+(.{0,100})\",\n content,\n re.IGNORECASE\n )\n\n for deprecation_msg in deprecated:\n # Prüfe ob Migration dokumentiert ist\n has_migration = bool(re.search(\n r\"use\\s+\\w+\\s+instead|migrate\\s+to|replacement|@see\",\n deprecation_msg,\n re.IGNORECASE\n ))\n\n if not has_migration:\n warnings.append(\n \"W10.6: @deprecated without migration path. \"\n \"Add 'Use XY instead' or '@see NewClass'.\"\n )\n break\n\n return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W10_1_AbstractFactoryWithoutDocs(),\n W10_2_EventDispatcherWithoutDocs(),\n W10_3_CacheWithoutStrategy(),\n W10_4_MultipleInheritanceWithoutDocs(),\n W10_5_ExternalServiceWithoutDocs(),\n W10_6_DeprecationWithoutMigration(),\n]\n",
"structuredPatch": [],
"originalFile": null
}
}