# AI-Optimized Python Style Guide

This document defines the Python style conventions for the Manual Slop codebase.
These deviate from PEP 8 / Google style to minimize token consumption when code
is processed by AI agents, while preserving readability for human review.

## 1. Indentation and Whitespace

- **Indentation:** 1 space per level. No tabs.
- **Continuation lines:** 1 space relative to the opening construct.
- **Blank lines:** Zero blank lines between function/method definitions within a class. One blank line between top-level definitions only when separating logically distinct sections.
- **Trailing whitespace:** None.
- **Rationale:** 1-space indentation reduces token count by ~40% compared to 4-space on deeply nested GUI code, with no loss of structural clarity for AST-based tools.

### Maximum Nesting Depth

- **Hard limit: 5 levels maximum.**
- AI agents consistently misinterpret Python scope via indentation. This hard limit prevents deeply nested blocks that confuse model interpretation.
- Classes and async handlers typically consume 1-2 levels before business logic begins, so 5 accommodates real-world usage patterns.
- **Enforcement:** Use ruff `[tool.ruff.lint.mccabe] max-complexity = 5` in the project linter config.
- **Refactoring mandate:** Any block exceeding 5 levels must be extracted into a named function. Do not "work around" this with tricks—extract the logic.
- **Rationale:** GUI callbacks and async handlers naturally want to nest. This constraint forces extraction of deep logic into testable, named units.

## 2. Type Annotations

- **All functions and methods** must have return type annotations.
- **All parameters** (except `self`/`cls`) must have type annotations.
- **Module-level and class-level variables** must have type annotations.
- **Use modern syntax:** `list[str]`, `dict[str, Any]`, `X | None` over `Optional[X]` where Python 3.10+ is available. Use `from __future__ import annotations` if needed.
- **Callable:** Use bare `Callable` for callback factories. Use `Callable[[ArgTypes], ReturnType]` when the signature is known and stable.
- **DearPyGui / ImGui callbacks:** Use `sender: Any, app_data: Any` for framework callbacks where the types are runtime-determined.

## 3. Imports

- Use `from __future__ import annotations` at the top of every module.
- Group imports: stdlib, third-party, local — separated by a blank line.
- Use `from typing import Any, Optional, Callable` etc. for type-only imports.
- Prefer `from x import Y` for specific symbols over `import x` when only one or two names are used.

## 4. Naming

- **snake_case** for modules, functions, methods, variables.
- **PascalCase** for classes.
- **ALL_CAPS** for module-level constants.
- **Single leading underscore** (`_name`) for internal/private members.

## 5. Docstrings

- Required on classes and non-trivial public functions.
- Use `"""triple double quotes"""`.
- One-line summary is sufficient for simple methods.
- Omit docstrings on obvious internal methods (e.g., `_cb_*` callbacks, `_render_*` UI methods) where the name is self-documenting.

## 6. String Formatting
- Prefer f-strings.
- Use double quotes (`"`) for strings by default.
- Use single quotes when the string contains double quotes.

## 7. Error Handling
- Never use bare `except:`.
- Use specific exception types.
- Prefer `if x is None:` over `if not x:` when testing for None specifically.

## 8. AI-Agent Specific Conventions

- **No redundant comments.** Do not add comments that restate what the code does. Only comment on *why* when non-obvious.
- **No empty `__init__.py` files.**
- **Minimal blank lines.** Token-efficient density is preferred over visual padding.
- **Short variable names are acceptable** in tight scopes (loop vars, lambdas). Use descriptive names for module-level and class attributes.
- **No diagnostic noise in production code (Added 2026-06-09).** `sys.stderr.write(f"[XYZ_DIAG] ...")` lines added to `src/*.py` for one-time debugging are technical debt the moment they ship. The project's production code should not contain `[XYZ_DIAG]` markers, `print(...debug...)` calls, or any other ad-hoc debug instrumentation. The right place for diagnostic output during a one-time investigation is `tests/artifacts/<test_name>.diag.log` (a log file) or a standalone `/tmp/diag_<name>.py` script. If you must instrument a production function for a single test run, the diag lines are part of the same atomic commit as the fix — they do not live uncommitted in the working tree. If you "revert everything," that means the diag lines are also reverted.
- **Test files ARE allowed to be diagnostic.** `tests/test_*.py` may use `print(..., file=sys.stderr)` freely for test output. The rule against diagnostic noise applies to `src/*.py` only.

## 10. Anti-OOP Conventions

### Philosophy

AI agents consistently misinterpret class hierarchies, method resolution, and inheritance. Flat function-call graphs are deterministic and traceable. OOP introduces scoping complexity that compounds with indentation.

### Hard Rules (Enforced by lint)

- **Never write a class for a single method.** Use a function.
- **Never use inheritance for code reuse.** Compose with standalone functions.
- **Never use private methods (`_method`).** Module-level functions with clear names suffice.
- **No nested classes.** Define helper types at module level.
- **No decorator classes.** Use plain functions with decorators.

### Class Justification Required

Every class definition MUST include a comment explaining WHY it is a class and not a function group or struct:

```python
# JUSTIFIED: Holds mutable shared state across multiple async operations
# Cannot be replaced by functions without passing state through every call
class AsyncOperationScheduler:
    ...

# NOT JUSTIFIED: Simply groups related functions
# Should be module-level functions in operations.py
class OperationHelper:
    def validate(self): ...
    def execute(self): ...
```

### Acceptability Criteria

A class is justified ONLY when ALL of:

1. It holds mutable state that must be encapsulated
2. It has 3+ related methods that share state
3. It implements a behavioral interface used polymorphically (not just data grouping)

### Refactoring Existing Classes (Strangler Fig Pattern)

When refactoring a class to functions:

1. Write test validating current behavior (prevents regression)
2. Extract one method at a time into module-level functions
3. Create wrapper function that delegates to class until migration complete
4. Delete class only when ALL callers migrated
5. Commit with `refactor(oop):` prefix

### Data Structures

- **Data-only containers:** Use `NamedTuple`, `dataclass(frozen=True)`, or plain `dict` — NOT classes
- **State machines:** Use dict-based transitions, not class + inheritance
- **Configuration:** Plain dict or `TypedDict`, not classes with defaults

### Anti-Patterns (Flagged by Ruff PLR rules)

- `PLR0912`: Too many branches — extract to functions
- `PLR6301`: No public methods — class is a namespace anti-pattern
- `PLR0206`: Descriptors in class body — use simple attributes

### Enforcement

```toml
[tool.ruff.lint.select]
select = ["E", "F", "W", "C90", "C4", "PLR0912", "PLR6301", "PLR0206"]

[tool.ruff.lint.plr]
max-returns = 4
max-locals = 8
max-args = 5
```

### 11. ImGui Defer Patterns

To prevent `PopID` or `End` leaks in immediate-mode rendering, and to keep code flat (0-1 levels of nesting) for AI agents, use the following patterns:

- **The Context Manager Pattern (Mandatory for complex blocks):**
  Wrap all `Begin/End` blocks in `imscope` context managers (from `src/imgui_scopes.py`).

  ```python
  with imscope.window("My Window") as (exp, opened):
   if exp:
    imgui.text("Hello")
  
  with imscope.tab_item("My Tab") as (exp, _):
   if exp:
    self._render_tab_content()
  ```

  This adds only 1 space of indentation (project standard) and guarantees the corresponding `End` is called even on early returns or exceptions. **Crucial:** Always check the `exp` (expanded/visible) state before rendering content to avoid ID conflicts and performance overhead.

- **The Flat Dispatch Pattern (Recommended for the main loop):**

  To avoid nesting multiple window checks, use a dispatch helper that encapsulates the state check and the scope.

  ```python
  self._render_window_if_open("My Window", self._render_my_panel)
  ```

  This keeps the main GUI loop as a flat sequence of declarative calls.

## 12. Structural Dependency Mapping (SDM)

To assist AI agents in evaluating refactoring impact across dynamic codebases, all major definitions SHOULD include terse SDM tags at the end of their docstrings.

- **Format:** Tags are enclosed in square brackets at the end of the docstring body.
- **For Functions/Methods:** `[C: CallerA, CallerB]` — List of primary internal callers within the codebase.
- **For State Variables:**
  - `[M: File:Line, Method]` — List of primary mutation points (where the value is assigned).
  - `[U: File]` — Major codepaths of use (where the value is read but not changed).

## 13. Extreme Vertical Compaction & Alignment

To minimize token usage and enhance visual scanning for human reviewers, heavily compact repetitive logic, especially in GUI definitions:

- **Single-Line Conditionals:** Prefer `if cond: do_this()` over multiline blocks for simple assignments or function calls. **Note:** Function and method definition signatures (`def ...:`) must ALWAYS remain on their own isolated lines and should never be compacted.
- **Semicolon Stacking:** Chain closely related framework calls on a single line using semicolons (e.g., `imgui.same_line(); imgui.text("Label")`).
- **Alignment:** Align assignments and inline comments vertically when declaring batches of related variables or conditionals.

  ```python
  if   status == 'running':  col = (0.0, 1.0, 0.0, 1.0)
  elif status == 'starting': col = (1.0, 1.0, 0.0, 1.0)
  elif status == 'error':    col = (1.0, 0.0, 0.0, 1.0)
  ```

## 14. Logical Region Blocks

For files where many related methods/properties live in a single class (e.g., the `App` class in `src/gui_2.py` holding global UI state; the `src/ai_client.py` module holding 8 vendor entry points and supporting machinery), use `#region: Section Name` and `#endregion: Section Name` tags (or `# --- Section Name ---` for visual grouping) to strictly organize methods and state properties. This establishes a predictable structure that MCP tools and agents can leverage for contextual masking.

**Removed anti-pattern (2026-06-11):** the prior version of this section said "extremely large files that violate the Anti-OOP rule by necessity." That framing was wrong. Files are not "large" in any absolute sense; production codebases (Unreal, OS kernels, game engines) routinely have 10K+ line files. The "Anti-OOP" rule is about data-vs-behavior separation, not file size. The `App` class in `src/gui_2.py` is not "violating" anything by being large; it's the natural shape of a class that owns the GUI orchestration. The `#region` convention is for navigability, not as a workaround for "files that got too big."

**Hard rule on new `src/<thing>.py` files (added 2026-06-11):** New namespaced `src/<thing>.py` files may only be created on the user's explicit request. If you find yourself about to create one, ASK FIRST — don't just create it. Rationale: the user is the only one who can authorize a new top-level namespace. Defaults: helpers and sub-systems go in the parent module. E.g., AI-client-specific helpers go in `src/ai_client.py`; app-controller helpers go in `src/app_controller.py`; MCP-client helpers go in `src/mcp_client.py`. Even if the parent file is already 3K+ lines, the helper still goes there. If a new top-level `src/<thing>.py` is genuinely warranted (e.g., a truly new system that doesn't fit any existing parent), propose it in the next checkpoint or status note and wait for the user's explicit "yes, create it." See `AGENTS.md` "File Size and Naming Convention" for the full rule.

## 15. Modular Controller Pattern

To prevent "God Object" bloat in core controllers (like `AppController`):

- **Extract Logic:** Move all state-independent or purely utility logic to module-level functions.
- **Dependency Injection:** Module-level functions that require class state should accept the instance as their first argument (e.g., `def my_extracted_logic(controller: AppController, ...)`).
- **Handler Maps:** Replace massive `if/elif` blocks (like those in event dispatchers) with dictionaries mapping keys to module-level handler functions.
- **Inner Class Extraction:** Never define nested classes or functions within methods. Move them to the module level.

## 16. See Also — Per-File Pattern Demonstrations

The following per-source-file guides show these conventions applied in real code:

- **[docs/guide_gui_2.md](../../../docs/guide_gui_2.md):** §"UI Delegation Pattern" — 90+ module-level `render_xxx(app)` functions in `src/gui_2.py`. Every panel uses the `App` instance as injected state, never `self`.
- **[docs/guide_app_controller.md](../../../docs/guide_app_controller.md):** §"Hook API Surface" — `_predefined_callbacks` and `_gettable_fields` as handler maps, not if/elif chains. §"AppState" — dataclass with all-typed fields, no methods.
- **[docs/guide_command_palette.md](../../../docs/guide_command_palette.md):** §"CommandRegistry" — decorator-based registration (`@registry.register`) instead of list-of-dicts. §"defensive try/except wrapping" — handler-map style error isolation.
- **[docs/guide_mcp_client.md](../../../docs/guide_mcp_client.md):** §"3-Layer Security Model" — handler-map dispatch (`def dispatch(tool_name, ...)`) with one branch per tool, instead of polymorphism.
- **[docs/guide_multi_agent_conductor.md](../../../docs/guide_multi_agent_conductor.md):** §"WorkerPool" — module-level `run_mma_worker` function, not a class method. The `WorkerPool` is justified (encapsulates semaphore + executor + active set).
- **[docs/guide_models.md](../../../docs/guide_models.md):** §"Data Structures" — `dataclass(frozen=True)` and `pydantic` over OOP classes for data-only containers.
