From 4eba059e899434b2bc1d03e192b60d3698181b87 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 9 Jun 2026 13:48:17 -0400 Subject: [PATCH] unfuck edit workflow. --- conductor/code_styleguides/python.md | 17 ++++++ conductor/edit_workflow.md | 80 +++++++++++++++------------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/conductor/code_styleguides/python.md b/conductor/code_styleguides/python.md index 640f2ad7..5982a9a5 100644 --- a/conductor/code_styleguides/python.md +++ b/conductor/code_styleguides/python.md @@ -71,9 +71,11 @@ is processed by AI agents, while preserving readability for human review. ## 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. @@ -81,6 +83,7 @@ AI agents consistently misinterpret class hierarchies, method resolution, and in - **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 @@ -97,13 +100,17 @@ class OperationHelper: ``` ### 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 @@ -111,16 +118,19 @@ When refactoring a class to functions: 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"] @@ -137,6 +147,7 @@ To prevent `PopID` or `End` leaks in immediate-mode rendering, and to keep code - **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: @@ -146,13 +157,17 @@ To prevent `PopID` or `End` leaks in immediate-mode rendering, and to keep code 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) @@ -172,6 +187,7 @@ To minimize token usage and enhance visual scanning for human reviewers, heavily - **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) @@ -185,6 +201,7 @@ For extremely large files that violate the "Anti-OOP" rule by necessity (e.g., ` ## 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. diff --git a/conductor/edit_workflow.md b/conductor/edit_workflow.md index 0fd98f32..bc22dc81 100644 --- a/conductor/edit_workflow.md +++ b/conductor/edit_workflow.md @@ -1,16 +1,20 @@ # Manual Slop Edit Tool Workflow ## The Problem + The `manual-slop_edit_file` tool requires **exact string matches** (character-for-character). Whitespace differences cause failures. The Python file uses **1-space indentation**. ## The Rules ### 1. ALWAYS Use Small, Incremental Edits + **WRONG:** Replace large blocks (50+ lines) **RIGHT:** Replace 3-10 lines at a time, verify, repeat ### 2. Verify Before Editing + Before ANY edit to a function you haven't touched recently: + ``` 1. Run: git checkout -- src/gui_2.py 2. Run: py_check_syntax on src/gui_2.py @@ -18,11 +22,13 @@ Before ANY edit to a function you haven't touched recently: ``` ### 3. Reading Before Editing (CRITICAL) + - Use `get_file_slice` to get the EXACT text including all whitespace - Copy text directly from the tool output - do NOT reformat - If using get_definition, verify the text matches before editing ### 4. The Edit Tool Parameters (snake_case) + ```python { "path": "src/gui_2.py", # Required: file path @@ -33,6 +39,7 @@ Before ANY edit to a function you haven't touched recently: ``` ### 5. 1-Space Indentation in Python + - Class methods: ` def` (0 spaces, then 1) - Method body: ` ` (2 spaces total) - Nested blocks: ` ` (3 spaces total) @@ -41,14 +48,17 @@ Before ANY edit to a function you haven't touched recently: ### 6. The Decorator-Orphan Pitfall (Added 2026-06-07) When inserting new methods **before an existing `@property` def**: -``` + +```python @property def perf_profiling_enabled(self) -> bool: ... ``` + If you anchor on `def perf_profiling_enabled` and insert before it, the `@property` decorator on the line above is left orphaned on the line right before YOUR new method. Now `@property` decorates your method (which is no longer a property), and the original setter `@perf_profiling_enabled.setter` blows up at import with `'function' object has no attribute 'setter'`. **Fix:** Anchor on a non-decorated landmark, or include the decorator in the replacement: + - `old_string` = ` self._init_actions()\n\n @property\n def perf_profiling_enabled` - `new_string` = ` self._init_actions()\n\n def your_new(...)\n ...\n\n @property\n def perf_profiling_enabled` @@ -57,6 +67,7 @@ This keeps the `@property` attached to its original method. ### 7. ast.parse() Is Not Enough (Added 2026-06-07) `py_check_syntax` only confirms `ast.parse()` succeeds. Semantic errors (wrong decorator targets, wrong base class, wrong attribute, missing `self`) are NOT caught. After any multi-line edit, ALWAYS: + 1. Import the module: `python -c "from src.app_controller import AppController"` 2. Instantiate the class 3. Call the new method in the way it's expected to be called (`ctrl.foo_ts` for a property, `ctrl.foo_ts()` for a method) @@ -67,39 +78,32 @@ This keeps the `@property` attached to its original method. ## Step-by-Step Workflow for gui_2.py -### Before ANY edit: -```powershell -git checkout -- src/gui_2.py -``` - ### Check current state: + ```powershell py_check_syntax path=src/gui_2.py get_file_slice path=src/gui_2.py start_line=X end_line=Y ``` ### For each edit: + 1. Make the smallest possible change (3-10 lines) 2. Run `py_check_syntax` to verify -3. If syntax error, immediately `git checkout -- src/gui_2.py` +3. If syntax error, immediately report to the user to address. 4. Only proceed if syntax is OK ### If edit fails with "old_string not found": + - The text you're trying to replace doesn't EXACTLY match - Use `get_file_slice` to get the exact text - Copy it character-for-character including whitespace - Try again with exact match -### If syntax error after edit: -```powershell -git checkout -- src/gui_2.py -``` -Then try again with smaller edit. - ## Alternative: Update Definition Approach For large function rewrites, use `py_update_definition`: -``` + +```md name: function_name path: src/gui_2.py new_content: complete new function source @@ -110,48 +114,48 @@ This replaces the entire function at once using AST detection. ## Context Composition Requirements ### Current Broken State + Files & Media works. Context Composition needs: 1. Add state tracking at start of function: -```python -if not hasattr(self, 'ctx_files_open'): - self.ctx_files_open = True -if not hasattr(self, 'ctx_shots_open'): - self.ctx_shots_open = True -``` + + ```python + if not hasattr(self, 'ctx_files_open'): + self.ctx_files_open = True + if not hasattr(self, 'ctx_shots_open'): + self.ctx_shots_open = True + ``` 2. Files section with collapsing header and child window: -```python -if imgui.collapsing_header("Files", self.ctx_files_open): - imgui.begin_child("ctx_files_child", imgui.ImVec2(-1, 200), True) - # table code here - imgui.end_child() -``` + + ```python + if imgui.collapsing_header("Files", self.ctx_files_open): + imgui.begin_child("ctx_files_child", imgui.ImVec2(-1, 200), True) + # table code here + imgui.end_child() + ``` 3. Screenshots section with collapsing header and child window: -```python -if imgui.collapsing_header("Screenshots", self.ctx_shots_open): - imgui.begin_child("ctx_shots_child", imgui.ImVec2(-1, 100), True) - # screenshot list here - imgui.end_child() -``` + + ```python + if imgui.collapsing_header("Screenshots", self.ctx_shots_open): + imgui.begin_child("ctx_shots_child", imgui.ImVec2(-1, 100), True) + # screenshot list here + imgui.end_child() + ``` 4. Fixed presets bar with push_item_width(150) on the combo 5. Remove the batch action bar entirely (Full/Agg/Sig/Def/None/Sel All/Del buttons) ## Key Files + - `src/gui_2.py` - Main GUI (1-space indentation, CRLF) - `src/models.py` - Data models including FileItem - Context Composition function: line ~2748 ## Test Command + ```powershell uv run sloppy.py ``` - -## If Everything Goes Wrong -```powershell -git checkout -- src/gui_2.py -git checkout -- src/models.py -``` \ No newline at end of file