# Guide for AI Coding Assistants

Critical Ulauncher-specific patterns and constraints.

## Core Constraints

- **GTK main thread**: All GTK operations must run in the main thread. Use `scheduling.run_when_idle` to schedule work from other contexts.
- **Callback-based async**: Use GLib callbacks, not Python async/await. Use `scheduling.timer` for delayed execution or `scheduling.interval` if they need to repeat.
- **Lazy module loading**: Defer imports not needed for the initial window to keep startup fast.
- **Exception avoidance**: Prefer defensive code (`hasattr()`, guard clauses) over try/except when possible. Never catch bare `Exception` unless the exception type cannot be known.
- **Dict access**: Index directly (`d["key"]`) when the key is guaranteed by a known type or invariant. It reads clearer and enables type narrowing. Use `d.get()` when a missing key is a legitimate, expected case with a sensible default.

## Code Quality

**Comments**: Don't comment what the code changes does (tutorial-style). Write for future maintainers reading the full code without your context. Explain the parts you don't pick up from reading the code (gotchas, workarounds, unconventional solutions etc).

**Complexity**: Prefer straightforward and simple solutions, without architectural changes. But suggest alternatives if the code adds technical debt that can be improved by making it more modular, reusable, or easier to read or maintain.

**Testing & Linting**: Target Python 3.8+ with 3.10 type hints. Run `make check-all` for all linting and tests running inside both the user environment and the docker test image which uses Python 3.8. If you can't use docker, just run `make check`. Run `make format` to fix any formatting errors.

## Architecture

Use these patterns when applicable:

**EventBus** (`ulauncher.utils.eventbus.EventBus`) - Use for cross-module communication when modules can't directly reference each other (e.g., core notifying UI, registry notifying modes). See [docs/architecture/eventbus.md](docs/architecture/eventbus.md) for usage.

**BaseDataClass** (`ulauncher.data.BaseDataClass`) - Use instead of dataclasses. See [docs/architecture/base_data_class.md](docs/architecture/base_data_class.md) for examples.

**JsonConf** (`ulauncher.data.JsonConf`) - Use for config files that need auto-deduplication and safe concurrent access. See [docs/architecture/json_conf.md](docs/architecture/json_conf.md) for examples.

**JsonKeyValueConf** (`ulauncher.data.JsonKeyValueConf`) - Use for JSON files that are flat key/value dicts with uniformly-typed values. See [docs/architecture/json_key_value_conf.md](docs/architecture/json_key_value_conf.md) for examples.

**Modes** - Examine `Mode` and existing modes in `ulauncher/modes/` when adding new query handlers. See [docs/architecture/mode-system.md](docs/architecture/mode-system.md) for the interface.

**Extension IPC** - When modifying extension communication, see [docs/architecture/extension-ipc.md](docs/architecture/extension-ipc.md) for the multi-process architecture.

## Project Layout

```
ulauncher/
├── api/          # Extension API (separate process)
├── data/         # Core data classes (BaseDataClass, JsonConf, JsonKeyValueConf)
├── modes/        # Query handlers (apps, files, extensions, etc.)
├── ui/           # GTK components
└── utils/        # Shared utilities
```

Key files for extensions: `modes/extensions/extension_runtime.py`, `api/client/Client.py`, `utils/socket_msg_controller.py`
