{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/crawler\/planung\/01-code-standards.yaml",
"content": "---\n# ============================================================================\n# Crawler – Code-Standards & Quality Gateways\n# ============================================================================\n# Verbindlich. Keine Umgehung. Alle Gateways müssen vor Merge\/Deploy passen.\n# Stand: 2026-04-21\n# ============================================================================\n\nmeta:\n document: crawler-code-standards\n version: 1.0.0\n binding: true\n last_updated: 2026-04-21\n scope:\n - python_core: \/var\/www\/dev.campus.systemische-tools.de\/crawler\/src\n - php_presentation: \/var\/www\/dev.campus.systemische-tools.de\/src (Controller\/View unter Route \/crawler)\n\n# ----------------------------------------------------------------------------\n# Architektur\n# ----------------------------------------------------------------------------\narchitecture:\n paradigm: Domain-Driven Design\n style: Hexagonal (Ports and Adapters)\n core_principles: [SRP, SoC, DRY, DIP, YAGNI]\n\n layers:\n domain:\n responsibility: reine Geschäftslogik, Entities, Value Objects, Domain Services\n imports_allowed: [stdlib, typing, dataclasses, datetime, hashlib, enum, re, abc]\n imports_forbidden: [framework, db_driver, http_client, filesystem, third_party_business]\n application:\n responsibility: Use Cases, Orchestrierung, Ports (Interfaces zu Infrastructure)\n imports_allowed: [domain, stdlib]\n imports_forbidden: [infrastructure, interfaces, third_party_impl]\n infrastructure:\n responsibility: Adapter – DB, HTTP, Playwright, Filesystem, Logging\n imports_allowed: [domain, application, stdlib, third_party]\n imports_forbidden: [interfaces]\n interfaces:\n responsibility: CLI (Python), HTTP-Controller + Views (PHP), HTMX\n imports_allowed: [application, domain, stdlib, third_party_ui]\n imports_forbidden: [infrastructure_internals]\n\n dependency_direction: |\n interfaces → application → domain\n infrastructure → application, domain\n Verletzungen → Gate 4 Failure.\n\n# ----------------------------------------------------------------------------\n# Harte Code-Metriken (nicht verhandelbar, nicht umgehbar)\n# ----------------------------------------------------------------------------\ncode_metrics:\n max_loc_per_method:\n soft: 50\n hard: 80\n excludes_from_count: [comments, docstrings, blank_lines, pure_type_stubs]\n applies_to: [python_functions, python_methods, php_methods, php_functions]\n enforcement: gate_1_lint_block\n\n max_loc_per_class:\n soft: 50\n hard: 80\n excludes_from_count: [comments, docstrings, blank_lines, class_attribute_declarations]\n applies_to: [python_classes, php_classes]\n note: \"SRP ernst nehmen – große Adapter in fokussierte Sub-Adapter aufteilen.\"\n enforcement: gate_1_lint_block\n\n max_loc_per_file:\n hard: 200\n excludes_from_count: [comments, docstrings, blank_lines]\n enforcement: gate_1_lint_block\n\n max_cyclomatic_complexity:\n soft: 5\n hard: 8\n enforcement: gate_1_lint_block\n\n max_method_parameters:\n hard: 4\n note: \"Mehr Parameter → Value Object \/ DTO einführen.\"\n enforcement: gate_1_lint_block\n\n max_nesting_depth:\n hard: 3\n enforcement: gate_1_lint_block\n\n max_return_points_per_method:\n hard: 4\n enforcement: gate_1_lint_warn\n\n# ----------------------------------------------------------------------------\n# Naming\n# ----------------------------------------------------------------------------\nnaming:\n python:\n modules: snake_case\n classes: PascalCase\n functions: snake_case\n methods: snake_case\n constants: UPPER_SNAKE_CASE\n private_members: _leading_underscore\n type_aliases: PascalCase\n test_files: \"test_*.py\"\n php:\n namespaces: PascalCase\n classes: PascalCase\n interfaces: \"*Interface oder *Port\"\n methods: camelCase\n constants: UPPER_SNAKE_CASE\n properties: camelCase\n\n# ----------------------------------------------------------------------------\n# Verboten (hard fail)\n# ----------------------------------------------------------------------------\nforbidden:\n - id: F-001\n rule: Magic Numbers\n detail: \"Zahlenliterale außerhalb von 0, 1, -1 nur via Constants::* bzw. constants.*\"\n enforcement: gate_1_lint_block\n - id: F-002\n rule: Direkter SQL-Zugriff außerhalb Infrastructure-Repositories\n enforcement: gate_4_architecture_block\n - id: F-003\n rule: Framework-Imports in Domain\/Application\n enforcement: gate_4_architecture_block\n - id: F-004\n rule: Globaler veränderlicher Zustand (modul-level mutable state)\n enforcement: gate_1_lint_block\n - id: F-005\n rule: print() \/ var_dump() \/ echo-Debug\n detail: \"Ausschließlich Logging-Adapter verwenden.\"\n enforcement: gate_1_lint_block\n - id: F-006\n rule: Bare except \/ catch(\\Throwable) außerhalb Interfaces-Layer-Edge\n enforcement: gate_1_lint_block\n - id: F-007\n rule: Zirkuläre Imports\n enforcement: gate_1_lint_block\n - id: F-008\n rule: God-Classes (> max_loc_per_class.hard)\n enforcement: gate_1_lint_block\n - id: F-009\n rule: fetch() \/ XMLHttpRequest \/ jQuery.ajax im Frontend\n detail: \"Pflicht: HTMX gemäß CLAUDE.md HTMX-C1..C5.\"\n enforcement: gate_7_htmx_block\n - id: F-010\n rule: Secrets \/ Passwörter in Code oder Commands\n detail: \"Aus Environment via get_db_password() \/ getenv('DB_PASSWORD').\"\n enforcement: pre_hook_block\n - id: F-011\n rule: Mutating-Requests ohne CSRF (POST\/PUT\/PATCH\/DELETE)\n enforcement: gate_7_htmx_block\n - id: F-012\n rule: SELECT * in Repositories\n enforcement: gate_1_lint_block\n - id: F-013\n rule: Business-Logik in Controllers oder Views\n enforcement: gate_4_architecture_block\n - id: F-014\n rule: time.sleep() in Tests\n enforcement: gate_2_unit_block\n - id: F-015\n rule: Netzwerk-Zugriffe in Unit-Tests\n enforcement: gate_2_unit_block\n - id: F-016\n rule: Auskommentierter Code im Commit\n enforcement: gate_1_lint_block\n\n# ----------------------------------------------------------------------------\n# Pflicht (hard require)\n# ----------------------------------------------------------------------------\nrequired:\n python:\n type_hints:\n mode: strict\n tool: mypy --strict\n coverage_percent: 100\n entities_as: dataclass(frozen=True) oder pydantic.BaseModel\n errors: spezifische Exception-Klassen pro Domain-Fehler\n logging: structlog oder stdlib logging mit JSON-Formatter\n io_boundary_only: Dateisystem\/Netzwerk nur in Infrastructure-Adaptern\n docstrings:\n public_api: required\n private: optional\n php:\n declare_strict_types: true\n return_types: required\n param_types: required\n property_types: required\n phpstan_level: 8\n readonly_where_possible: true\n csrf_all_mutating: true\n htmx_headers_pattern: gemäß CLAUDE.md Standard-Pattern\n\n# ----------------------------------------------------------------------------\n# Testing\n# ----------------------------------------------------------------------------\ntesting:\n coverage:\n project_min_percent: 85\n domain_layer_min_percent: 95\n application_layer_min_percent: 90\n naming_pattern: \"test_<method>_<scenario>_<expected_result>\"\n structure:\n - tests\/unit\/domain\n - tests\/unit\/application\n - tests\/integration\/infrastructure\n - tests\/e2e\n constraints:\n no_sleep: true\n no_network_in_unit: true\n no_db_in_unit: true\n fixtures: pytest fixtures \/ PHPUnit data providers\n required_for_each:\n - domain_entity: Konstruktions-, Invarianten- und Equality-Tests\n - use_case: mindestens Happy-Path + ein Fehlerpfad\n - adapter: Integrationstest gegen reale Ressource (DB\/HTTP-Fixture)\n\n# ----------------------------------------------------------------------------\n# DB \/ Persistenz\n# ----------------------------------------------------------------------------\npersistence:\n db_access: ausschließlich via MCP-DB oder infrastructure\/Repository-Adapter\n no_orm_in_domain: true\n migrations: versioniert unter src\/db\/migrations\/*.sql\n transactions: pro Use Case genau eine Transaktion\n bulk_insert_threshold: 100\n\n# ----------------------------------------------------------------------------\n# Quality Gateways (verbindlich; alle grün vor Deploy)\n# ----------------------------------------------------------------------------\nquality_gateways:\n policy: all_must_pass\n execution: script \/var\/www\/dev.campus.systemische-tools.de\/crawler\/scripts\/check.sh\n\n gates:\n - id: G1\n name: Static Analysis\n blocking: true\n python:\n - \"ruff check --select=ALL --ignore=D100,D104 src tests\"\n - \"ruff format --check src tests\"\n - \"mypy --strict src\"\n - \"radon cc -s -n B src\"\n - \"radon mi -s -n B src\"\n - \"python scripts\/max_loc_linter.py src\" # eigenes Tool: Klassen- und Methoden-LOC ohne Kommentare\/Blanks\n php:\n - \"\/var\/www\/scripts\/php-check.sh\"\n - \"phpstan analyse --level=8 src\/Controller\/Crawler src\/Domain\/Crawler src\/Infrastructure\/Crawler src\/View\/crawler\"\n - \"php-cs-fixer fix --dry-run --diff\"\n - \"php scripts\/max_loc_linter.php src\/Controller\/Crawler src\/Infrastructure\/Crawler\"\n\n - id: G2\n name: Unit Tests\n blocking: true\n python: \"pytest tests\/unit -q --cov=src --cov-fail-under=85\"\n php: \"phpunit --testsuite=crawler-unit --coverage-text\"\n\n - id: G3\n name: Integration Tests\n blocking: true\n preconditions: [crawler DB erreichbar, Playwright Chromium installiert]\n python: \"pytest tests\/integration -q\"\n\n - id: G4\n name: Architecture \/ Dependency Direction\n blocking: true\n python: \"import-linter --config .import-linter\"\n php: \"deptrac analyse deptrac.yaml\"\n\n - id: G5\n name: Security\n blocking: true\n tools:\n - \"semgrep --config=auto --error\"\n - \"pip-audit --strict\"\n - \"composer audit\"\n - \"gitleaks detect --no-git --source src --redact\"\n\n - id: G6\n name: DB-Schema-Drift\n ... [TRUNCATED-d1001eae1053eb7d]"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/crawler\/planung\/01-code-standards.yaml",
"content": "---\n# ============================================================================\n# Crawler – Code-Standards & Quality Gateways\n# ============================================================================\n# Verbindlich. Keine Umgehung. Alle Gateways müssen vor Merge\/Deploy passen.\n# Stand: 2026-04-21\n# ============================================================================\n\nmeta:\n document: crawler-code-standards\n version: 1.0.0\n binding: true\n last_updated: 2026-04-21\n scope:\n - python_core: \/var\/www\/dev.campus.systemische-tools.de\/crawler\/src\n - php_presentation: \/var\/www\/dev.campus.systemische-tools.de\/src (Controller\/View unter Route \/crawler)\n\n# ----------------------------------------------------------------------------\n# Architektur\n# ----------------------------------------------------------------------------\narchitecture:\n paradigm: Domain-Driven Design\n style: Hexagonal (Ports and Adapters)\n core_principles: [SRP, SoC, DRY, DIP, YAGNI]\n\n layers:\n domain:\n responsibility: reine Geschäftslogik, Entities, Value Objects, Domain Services\n imports_allowed: [stdlib, typing, dataclasses, datetime, hashlib, enum, re, abc]\n imports_forbidden: [framework, db_driver, http_client, filesystem, third_party_business]\n application:\n responsibility: Use Cases, Orchestrierung, Ports (Interfaces zu Infrastructure)\n imports_allowed: [domain, stdlib]\n imports_forbidden: [infrastructure, interfaces, third_party_impl]\n infrastructure:\n responsibility: Adapter – DB, HTTP, Playwright, Filesystem, Logging\n imports_allowed: [domain, application, stdlib, third_party]\n imports_forbidden: [interfaces]\n interfaces:\n responsibility: CLI (Python), HTTP-Controller + Views (PHP), HTMX\n imports_allowed: [application, domain, stdlib, third_party_ui]\n imports_forbidden: [infrastructure_internals]\n\n dependency_direction: |\n interfaces → application → domain\n infrastructure → application, domain\n Verletzungen → Gate 4 Failure.\n\n# ----------------------------------------------------------------------------\n# Harte Code-Metriken (nicht verhandelbar, nicht umgehbar)\n# ----------------------------------------------------------------------------\ncode_metrics:\n max_loc_per_method:\n soft: 50\n hard: 80\n excludes_from_count: [comments, docstrings, blank_lines, pure_type_stubs]\n applies_to: [python_functions, python_methods, php_methods, php_functions]\n enforcement: gate_1_lint_block\n\n max_loc_per_class:\n soft: 50\n hard: 80\n excludes_from_count: [comments, docstrings, blank_lines, class_attribute_declarations]\n applies_to: [python_classes, php_classes]\n note: \"SRP ernst nehmen – große Adapter in fokussierte Sub-Adapter aufteilen.\"\n enforcement: gate_1_lint_block\n\n max_loc_per_file:\n hard: 200\n excludes_from_count: [comments, docstrings, blank_lines]\n enforcement: gate_1_lint_block\n\n max_cyclomatic_complexity:\n soft: 5\n hard: 8\n enforcement: gate_1_lint_block\n\n max_method_parameters:\n hard: 4\n note: \"Mehr Parameter → Value Object \/ DTO einführen.\"\n enforcement: gate_1_lint_block\n\n max_nesting_depth:\n hard: 3\n enforcement: gate_1_lint_block\n\n max_return_points_per_method:\n hard: 4\n enforcement: gate_1_lint_warn\n\n# ----------------------------------------------------------------------------\n# Naming\n# ----------------------------------------------------------------------------\nnaming:\n python:\n modules: snake_case\n classes: PascalCase\n functions: snake_case\n methods: snake_case\n constants: UPPER_SNAKE_CASE\n private_members: _leading_underscore\n type_aliases: PascalCase\n test_files: \"test_*.py\"\n php:\n namespaces: PascalCase\n classes: PascalCase\n interfaces: \"*Interface oder *Port\"\n methods: camelCase\n constants: UPPER_SNAKE_CASE\n properties: camelCase\n\n# ----------------------------------------------------------------------------\n# Verboten (hard fail)\n# ----------------------------------------------------------------------------\nforbidden:\n - id: F-001\n rule: Magic Numbers\n detail: \"Zahlenliterale außerhalb von 0, 1, -1 nur via Constants::* bzw. constants.*\"\n enforcement: gate_1_lint_block\n - id: F-002\n rule: Direkter SQL-Zugriff außerhalb Infrastructure-Repositories\n enforcement: gate_4_architecture_block\n - id: F-003\n rule: Framework-Imports in Domain\/Application\n enforcement: gate_4_architecture_block\n - id: F-004\n rule: Globaler veränderlicher Zustand (modul-level mutable state)\n enforcement: gate_1_lint_block\n - id: F-005\n rule: print() \/ var_dump() \/ echo-Debug\n detail: \"Ausschließlich Logging-Adapter verwenden.\"\n enforcement: gate_1_lint_block\n - id: F-006\n rule: Bare except \/ catch(\\Throwable) außerhalb Interfaces-Layer-Edge\n enforcement: gate_1_lint_block\n - id: F-007\n rule: Zirkuläre Imports\n enforcement: gate_1_lint_block\n - id: F-008\n rule: God-Classes (> max_loc_per_class.hard)\n enforcement: gate_1_lint_block\n - id: F-009\n rule: fetch() \/ XMLHttpRequest \/ jQuery.ajax im Frontend\n detail: \"Pflicht: HTMX gemäß CLAUDE.md HTMX-C1..C5.\"\n enforcement: gate_7_htmx_block\n - id: F-010\n rule: Secrets \/ Passwörter in Code oder Commands\n detail: \"Aus Environment via get_db_password() \/ getenv('DB_PASSWORD').\"\n enforcement: pre_hook_block\n - id: F-011\n rule: Mutating-Requests ohne CSRF (POST\/PUT\/PATCH\/DELETE)\n enforcement: gate_7_htmx_block\n - id: F-012\n rule: SELECT * in Repositories\n enforcement: gate_1_lint_block\n - id: F-013\n rule: Business-Logik in Controllers oder Views\n enforcement: gate_4_architecture_block\n - id: F-014\n rule: time.sleep() in Tests\n enforcement: gate_2_unit_block\n - id: F-015\n rule: Netzwerk-Zugriffe in Unit-Tests\n enforcement: gate_2_unit_block\n - id: F-016\n rule: Auskommentierter Code im Commit\n enforcement: gate_1_lint_block\n\n# ----------------------------------------------------------------------------\n# Pflicht (hard require)\n# ----------------------------------------------------------------------------\nrequired:\n python:\n type_hints:\n mode: strict\n tool: mypy --strict\n coverage_percent: 100\n entities_as: dataclass(frozen=True) oder pydantic.BaseModel\n errors: spezifische Exception-Klassen pro Domain-Fehler\n logging: structlog oder stdlib logging mit JSON-Formatter\n io_boundary_only: Dateisystem\/Netzwerk nur in Infrastructure-Adaptern\n docstrings:\n public_api: required\n private: optional\n php:\n declare_strict_types: true\n return_types: required\n param_types: required\n property_types: required\n phpstan_level: 8\n readonly_where_possible: true\n csrf_all_mutating: true\n htmx_headers_pattern: gemäß CLAUDE.md Standard-Pattern\n\n# ----------------------------------------------------------------------------\n# Testing\n# ----------------------------------------------------------------------------\ntesting:\n coverage:\n project_min_percent: 85\n domain_layer_min_percent: 95\n application_layer_min_percent: 90\n naming_pattern: \"test_<method>_<scenario>_<expected_result>\"\n structure:\n - tests\/unit\/domain\n - tests\/unit\/application\n - tests\/integration\/infrastructure\n - tests\/e2e\n constraints:\n no_sleep: true\n no_network_in_unit: true\n no_db_in_unit: true\n fixtures: pytest fixtures \/ PHPUnit data providers\n required_for_each:\n - domain_entity: Konstruktions-, Invarianten- und Equality-Tests\n - use_case: mindestens Happy-Path + ein Fehlerpfad\n - adapter: Integrationstest gegen reale Ressource (DB\/HTTP-Fixture)\n\n# ----------------------------------------------------------------------------\n# DB \/ Persistenz\n# ----------------------------------------------------------------------------\npersistence:\n db_access: ausschließlich via MCP-DB oder infrastructure\/Repository-Adapter\n no_orm_in_domain: true\n migrations: versioniert unter src\/db\/migrations\/*.sql\n transactions: pro Use Case genau eine Transaktion\n bulk_insert_threshold: 100\n\n# ----------------------------------------------------------------------------\n# Quality Gateways (verbindlich; alle grün vor Deploy)\n# ----------------------------------------------------------------------------\nquality_gateways:\n policy: all_must_pass\n execution: script \/var\/www\/dev.campus.systemische-tools.de\/crawler\/scripts\/check.sh\n\n gates:\n - id: G1\n name: Static Analysis\n blocking: true\n python:\n - \"ruff check --select=ALL --ignore=D100,D104 src tests\"\n - \"ruff format --check src tests\"\n - \"mypy --strict src\"\n - \"radon cc -s -n B src\"\n - \"radon mi -s -n B src\"\n - \"python scripts\/max_loc_linter.py src\" # eigenes Tool: Klassen- und Methoden-LOC ohne Kommentare\/Blanks\n php:\n - \"\/var\/www\/scripts\/php-check.sh\"\n - \"phpstan analyse --level=8 src\/Controller\/Crawler src\/Domain\/Crawler src\/Infrastructure\/Crawler src\/View\/crawler\"\n - \"php-cs-fixer fix --dry-run --diff\"\n - \"php scripts\/max_loc_linter.php src\/Controller\/Crawler src\/Infrastructure\/Crawler\"\n\n - id: G2\n name: Unit Tests\n blocking: true\n python: \"pytest tests\/unit -q --cov=src --cov-fail-under=85\"\n php: \"phpunit --testsuite=crawler-unit --coverage-text\"\n\n - id: G3\n name: Integration Tests\n blocking: true\n preconditions: [crawler DB erreichbar, Playwright Chromium installiert]\n python: \"pytest tests\/integration -q\"\n\n - id: G4\n name: Architecture \/ Dependency Direction\n blocking: true\n python: \"import-linter --config .import-linter\"\n php: \"deptrac analyse deptrac.yaml\"\n\n - id: G5\n name: Security\n blocking: true\n tools:\n - \"semgrep --config=auto --error\"\n - \"pip-audit --strict\"\n - \"composer audit\"\n - \"gitleaks detect --no-git --source src --redact\"\n\n - id: G6\n name: DB-Schema-Drift\n ... [TRUNCATED-d1001eae1053eb7d]",
"structuredPatch": [],
"originalFile": null,
"userModified": false
}
}