Coding Instructions for AI Agents¶
GehaSoftwareHub - Project Standards & Best Practices
Purpose¶
This document provides clear instructions for AI coding agents (Claude Code, GitHub Copilot, etc.) working on this project. Following these guidelines ensures consistent, high-quality code that adheres to project standards.
Before writing ANY code, review this document and the referenced guides.
Core Principles¶
"Write code that is correct, maintainable, well-documented, and testable - in that order."
Quality over speed. Readable over clever. Explicit over implicit.
Required Reading: Project Guides¶
Read these guides BEFORE writing code (located in docs/dev_guidelines/):
- python_style_guide.md - PEP 8 compliance, naming, imports. CRITICAL
- pyside6_architecture_guide.md - Signal/Slot, MVC, threading, memory. CRITICAL for PySide6 code
- refactoring_import_guide.md - Import migration rules (src/ only). CRITICAL during refactor
- path_management_guide.md - PathDef system, get_path()/get_path_str(). HIGH
- constants_management_guide.md - Magic numbers, enums, config. HIGH
- file_operations_guide.md - Atomic file I/O, thread safety. HIGH
- pragmatic_testing_guide.md - What/how to test. HIGH
- python_documentation_guide.md - Google style docstrings, type hints. HIGH
Project-Specific Rules (CLAUDE.md)¶
These rules are non-negotiable and override any generic best practice:
Language Rules¶
- User-facing text: Always in German. Use direct umlauts: o, a, u (never oe, ae, ue)
- Code comments: Always in English
- Docstrings: Always in English (Google style)
Code Rules¶
- No Unicode symbols: Use text-based indicators instead. Not even in test scripts
- Empty
__init__.pyfiles: Never put any code in__init__.py - No hardcoded QSS stylesheets: Give every UI element a meaningful
.setObjectName(). Styling is handled externally viaStylesheetManager - Logger: Always local import of
get_logger(), never global
# CORRECT - Local import
def some_method(self) -> None:
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger()
logger.debug("Something happened")
# WRONG - Global import
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger() # Never at module level
- Tooltips: Use sparingly, only when truly beneficial. Prefer descriptive button/label names
- File operations: Follow
file_operations_guide.mdstrictly
Project Architecture¶
Directory Structure¶
GehaSoftwareHub/
+-- src/ # All refactored source code
| +-- custom_widgets/ # Reusable UI widgets
| | +-- constants/
| | +-- controls/ # SmartBackButton, etc.
| | +-- dialogs/ # User guide dialog, etc.
| | +-- feedback/ # Feedback widgets
| | +-- widget_handlers/ # Splitter, stacked widget managers
| |
| +-- main_hub/ # Main application
| | +-- features/ # Feature modules (MVC each)
| | | +-- home_system/ # Home dashboard
| | | | +-- bound_subsystems/ # GitHub issues, milestones
| | | +-- settings_system/ # Application settings
| | | +-- software_system/ # Software selection hub
| | | +-- update_system/ # Auto-update system
| | | +-- user_guide_system/ # User guide
| | +-- orchestration/ # Main app coordination
| | | +-- constants/ # View indices, etc.
| | | +-- controller/ # MainController
| | | +-- view/ # MainView
| | +-- ux_testing_environment/ # Developer testing tools
| |
| +-- shared_services/ # Shared infrastructure
| +-- cloud_com/ # Cloud communication
| | +-- github_com/ # GitHub API client
| | +-- r2_com/ # R2 storage
| +-- constants/ # App constants, PathDef definitions
| +-- file_operations/ # Atomic file I/O (JSON, msgpack, text)
| +-- logging/ # AsyncAppLogger
| +-- path_management/ # PathDef resolution system
| +-- prompt_dialogs/ # Dialog system (info, warning, error, input)
| +-- rendering/ # Visual rendering
| | +-- documents/ # Markdown/document rendering
| | +-- icons/ # IconRegistry, SVG rendering
| | +-- stylesheets/ # StylesheetManager, theme system
| +-- security/ # OAuth, encryption, login
| +-- settings/ # SettingsManager
| +-- state/ # AppState singleton
| +-- utils/ # Shared utilities
|
| +-- modules/ # Domain modules
| +-- plant_design/ # Plant design module
| +-- startup_system/ # Project launcher, library management
| +-- zentral_systems/ # Shared systems (library, planning, schema, etc.)
| +-- pd_runtime_system/ # Runtime stages (planning, engineering, etc.)
|
+-- data/
| +-- .app_data/ # App resources (REPLACEABLE on update)
| | +-- icons/ # SVG icons
| | +-- stylesheets/ # QSS stylesheets per module
| +-- persistent_data/ # User data (PROTECTED on update)
|
+-- docs/
+-- dev_guidelines/ # This directory
Architecture Pattern: MVC¶
Each feature module follows Model-View-Controller separation:
feature_system/
+-- controller/ # Coordinates model and view
+-- model/ # Business logic, data (optional)
+-- view/ # UI widgets
| +-- pyui/ # Auto-generated Python from .ui files
| +-- ui/ # Qt Designer .ui files
+-- constants/ # Feature-specific constants (optional)
+-- paths.py # Feature-specific PathDef definitions
Rules: 1. Models: Pure business logic. Only QObject/Signal from PySide6 2. Views: UI only, emit signals for user actions. No business logic 3. Controllers: Coordinate models and views, handle application logic
Import Patterns¶
All refactored code must import from src/ only¶
# Standard library
from typing import Optional, List
# Third-party
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtWidgets import QWidget
# Local application (always absolute from src.)
from src.shared_services.rendering.icons.api import IconRegistry, Icons, IconColors
from src.shared_services.rendering.stylesheets.api import StylesheetManager
from src.shared_services.prompt_dialogs.api import show_info, show_warning
from src.shared_services.file_operations.api import save_json, load_json
from src.shared_services.state.app_state import AppState
See refactoring_import_guide.md for the complete migration table.
Key Project Systems¶
1. Icons - IconRegistry (Theme-Aware)¶
from src.shared_services.rendering.icons.api import IconRegistry, Icons, IconColors
# Primary API: Register for automatic theme updates
registry = IconRegistry.instance()
registry.register(my_button, Icons.Action.Save, color=IconColors.Primary)
# For QLabels, use as_pixmap=True
registry.register(my_label, Icons.Status.Info, color=IconColors.Primary, as_pixmap=True)
# For dynamic icons, unregister before re-registering
registry.unregister(my_button)
registry.register(my_button, Icons.Action.Edit, color=IconColors.Accent)
Important: render_svg() and set_widget_icon() are low-level and do NOT update on theme change. Use IconRegistry.register() as the primary API.
2. Stylesheets - StylesheetManager¶
from src.shared_services.rendering.stylesheets.api import StylesheetManager
# Register widgets with their stylesheet PathDefs
manager = StylesheetManager.instance()
manager.register(my_widget, [MyModule.Paths.Stylesheet])
# Never hardcode QSS in Python code. Use .setObjectName() and external .qss files
my_widget.setObjectName("mySpecificWidget")
3. Paths - PathDef System¶
from src.shared_services.constants.paths import Logging, Icons
from src.shared_services.path_management.api import get_path, get_path_str
# pathlib.Path for filesystem operations
log_dir = get_path(Logging.Directory)
# String for Qt APIs
icon_str = get_path_str(Icons.Logos.GitHub)
4. File Operations - Atomic I/O¶
from src.shared_services.file_operations.api import save_json, load_json
# Always use these instead of raw open()/json.load()
data = load_json(MyPaths.ConfigFile)
save_json(MyPaths.ConfigFile, data)
5. Prompt Dialogs¶
from src.shared_services.prompt_dialogs.api import show_info, show_warning, show_error, ask_question
show_info("Erfolgreich gespeichert.", "Info", parent_widget)
show_warning("Ungespeicherte Aenderungen!", "Warnung", parent_widget)
result = ask_question("Moechten Sie fortfahren?", "Bestaetigung", parent_widget)
6. Application State¶
from src.shared_services.state.app_state import AppState
state = AppState.instance()
is_active = state.is_software_active
state.set_active_software("Plant Design")
7. Logging¶
# Always import locally, never at module level
def my_method(self) -> None:
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger()
logger.info("Operation completed")
Code Quality Requirements¶
1. Type Hints - MANDATORY¶
All function signatures MUST have type hints:
def calculate_total(self, quantity: int, discount: float = 0.0) -> float:
"""Calculate total with optional discount."""
return quantity * self._unit_price * (1 - discount)
2. Docstrings - Google Style¶
All public classes, methods, and functions MUST have docstrings:
def add_item(self, item: str) -> None:
"""Add an item to the collection.
Args:
item: The item to add. Must not be empty.
Raises:
ValueError: If item is empty or already exists.
"""
3. PySide6 Signal/Slot Requirements¶
class MyController(QObject):
"""Controller with proper signal/slot patterns."""
# Signal type annotations
data_changed: Signal = Signal(list)
error_occurred: Signal = Signal(str)
@Slot()
def on_button_clicked(self) -> None:
"""Handle button click."""
self._process_data()
@Slot(str)
def on_data_received(self, data: str) -> None:
"""Handle received data."""
self._update_model(data)
4. Error Handling¶
Use specific exceptions, never bare except:
try:
result = operation()
except ValueError as e:
from src.shared_services.logging.logger_factory import get_logger
get_logger().error(f"Validation failed: {e}")
except FileNotFoundError as e:
from src.shared_services.prompt_dialogs.api import show_error
show_error(f"Datei nicht gefunden: {e}", "Fehler", self)
5. Background Workers¶
Use QRunnable with a nested Signals class:
from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal
class MyWorker(QRunnable):
"""Background worker for heavy operations."""
class Signals(QObject):
finished: Signal = Signal()
error: Signal = Signal(str)
result: Signal = Signal(object)
def __init__(self) -> None:
super().__init__()
self.signals = self.Signals()
def run(self) -> None:
try:
result = self._do_work()
self.signals.result.emit(result)
except Exception as e:
self.signals.error.emit(str(e))
finally:
self.signals.finished.emit()
# Usage
worker = MyWorker()
worker.signals.result.connect(self._on_result)
QThreadPool.globalInstance().start(worker)
Anti-Patterns to AVOID¶
1. Hardcoded Stylesheets¶
# WRONG
button.setStyleSheet("background-color: #2196F3; color: white;")
# CORRECT
button.setObjectName("primaryButton")
# Styling handled in external .qss file via StylesheetManager
2. Global Logger¶
# WRONG
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger()
# CORRECT - import locally
def my_method(self) -> None:
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger()
3. Raw File I/O for App Data¶
# WRONG
with open("data/config.json", "r") as f:
config = json.load(f)
# CORRECT
from src.shared_services.file_operations.api import load_json
config = load_json(MyPaths.ConfigFile)
4. Unicode Symbols¶
# WRONG
status_text = "Status: \u2714 Erfolgreich"
# CORRECT
status_text = "Status: [OK] Erfolgreich"
5. Code in init.py¶
# WRONG - __init__.py with imports
from .my_module import MyClass
__all__ = ["MyClass"]
# CORRECT - empty __init__.py
# (leave the file completely empty)
6. Non-Theme-Aware Icons¶
# WRONG - Does not update on theme change
from src.shared_services.rendering.icons.api import render_svg
button.setIcon(render_svg(Icons.Action.Save, size=24))
# CORRECT - Theme-aware
registry = IconRegistry.instance()
registry.register(button, Icons.Action.Save, color=IconColors.Primary)
Code Review Checklist¶
Before submitting code, verify:
General¶
- [ ] All functions have type hints
- [ ] All public APIs have Google-style docstrings
- [ ] No bare
exceptclauses - [ ] Comments in English, user-facing text in German (with direct umlauts)
- [ ] No Unicode symbols anywhere
Project Systems¶
- [ ] Icons use
IconRegistry.register()(not raw render_svg for widgets) - [ ] No hardcoded QSS - use
.setObjectName()+ external stylesheets - [ ] Paths use PathDef constants with
get_path()/get_path_str() - [ ] File I/O uses
file_operations.api(not raw open/json) - [ ] Dialogs use
prompt_dialogs.apifunctions - [ ] Logger imported locally, never globally
- [ ]
__init__.pyfiles are empty
PySide6¶
- [ ] Signals have type annotations (
Signal = Signal(type)) - [ ] Slots use
@Slotdecorator - [ ] Connection types specified when not AutoConnection
- [ ] Memory cleanup implemented (deleteLater, parent-child)
- [ ] No GUI access from worker threads
Version: 2.1 Last Updated: 2026-03-19 Maintainer: Max (Project Owner)