Private
Public Access
0
0
Files
manual_slop/conductor/code_styleguides/python.md
T
ed 51edbdef20 docs(workflow,agents): remove 'large files are bad' propaganda; add naming rule
The user called out the LLM training data bias: 'small files are
good, large files are bad.' This is wrong for production codebases.
Unreal has 15K+ line files; OS kernels, game engines, compilers all
routinely have 10K+ line files. File size is a non-issue. Cognitive
load is managed via naming, regions, and navigation tools (the
manual-slop MCP) — NOT via file splitting.

Updates:

1. AGENTS.md (master agent guidance):
   - Added 'File Size and Naming Convention' section
   - Added the hard rule: '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.'
   - Defaults: helpers and sub-systems go in the parent module

2. conductor/workflow.md (Guiding Principles):
   - Removed 'Do NOT perform large file writes directamente' from
     principle 7 (it was a delegating rule, but 'large file writes'
     carried the propaganda)
   - Added principle 8: 'File Naming Convention (HARD RULE)' that
     references AGENTS.md
   - Re-phrased principle 9 (Research-First) to clarify it's about
     navigation efficiency, not file size

3. conductor/code_styleguides/python.md:
   - Removed the 'extremely large files that violate the Anti-OOP
     rule by necessity' framing
   - Added the new rule about new src/<thing>.py files

4. .opencode/agents/tier3-worker.md and .opencode/agents/tier4-qa.md:
   - Re-phrased 'Do NOT read full large files' to 'Use skeleton
     tools to navigate any file regardless of size. File size is
     not a concern; the right tools are.'
   - Added the new rule about not creating new src/<thing>.py
     files unless user explicitly requests it

5. conductor/tracks/qwen_llama_grok_followup_20260611/plan.md:
   - Updated the 'Naming Convention' section to reference the new
     'user explicit request' rule

This is docs-only. No code changes. The rule is now codified:
agents must ASK FIRST before creating new top-level src/ files.
2026-06-11 10:07:07 -04:00

14 KiB

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:

# 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

[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).

    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.

    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.

    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: §"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: §"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: §"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: §"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: §"WorkerPool" — module-level run_mma_worker function, not a class method. The WorkerPool is justified (encapsulates semaphore + executor + active set).
  • docs/guide_models.md: §"Data Structures" — dataclass(frozen=True) and pydantic over OOP classes for data-only containers.