Private
Public Access
0
0
Files
manual_slop/conductor/edit_workflow.md
T
ed e80d2952bc docs(harness+conductor): cover 5 missing styleguides in harvest; cosmetic cleanups
directive_hotswap_harness_20260627/spec.md: added the 5 missing
conductor/code_styleguides/*.md files to the harvest source list
(config_state_owner, workspace_paths, test_sandbox, chroma_cache,
code_path_audit) — the original list named 9 of 14. Added note that
the harvester should verify each contains a harvestable directive
before creating v1.md. Also verified tier2-autonomous.md path
exists (17,940 bytes) for plan Step 2.7.

tech-stack.md: removed duplicate src/paths.py entry (the full
description at line 37 already covers it; the line-73 stub was
redundant).

edit_workflow.md: Key Files section now points to src/project_files.py
for FileItem (moved out of src/models.py per the module taxonomy
refactor) and notes the line ~2748 ref may drift.
2026-07-02 19:05:43 -04:00

8.6 KiB

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: py_check_syntax on src/<file>.py
2. Get current state with get_file_slice (the exact lines you're about to touch)
3. Read the contract: does this function/field/method's signature, yield shape, or return type have callers I need to update?

DO NOT use git checkout or git restore to "revert" your way to a clean state. That destroys in-progress work. If a previous edit left the file in a broken state, ask the user.

3. Reading Before Editing (CRITICAL)

  • Use get_file_slice to get the EXACT text including all whitespace and EOL
  • Copy text directly from the tool output - do NOT reformat
  • If using get_definition, verify the text matches before editing
  • For set_file_slice: confirm the exact start_line and end_line (1-indexed, inclusive) by reading the file first. Off-by-one is a common silent failure.

4. The Edit Tool Parameters (snake_case)

{
  "path": "src/gui_2.py",      # Required: file path
  "old_string": "exact text",    # Required: must match EXACTLY
  "new_string": "replacement",  # Required: replacement text
  "replace_all": false           # Optional: replace all occurrences
}

5. 1-Space Indentation in Python

  • Class methods: def (0 spaces, then 1)
  • Method body: (2 spaces total)
  • Nested blocks: (3 spaces total)
  • NO 4-space indentation anywhere in this file

6. The Decorator-Orphan Pitfall (Added 2026-06-07)

When inserting new methods before an existing @property def:

@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

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)

8. set_file_slice IS Valid for Multi-Line Content (Revised 2026-06-09)

The previous rule ("Do not use set_file_slice for multi-line content") was wrong. set_file_slice does literal line replacement by design and is the right tool for 3-10 line surgical edits.

When to use which tool:

  • set_file_slice for surgical 3-10 line edits where you know the exact line range. Verify the line range with get_file_slice first. The start_line and end_line are 1-indexed and inclusive. The new content must reproduce the line count exactly (or be a precise replacement of the same N lines).
  • manual-slop_edit_file for exact-string replacement when you don't know the line range, or when the edit has a unique anchor string.
  • py_update_definition for whole-function replacement (AST-detected).
  • py_add_def for adding a new method/class to a class.
  • py_remove_def for removing a method/class.

The contract-change check (mandatory for any edit that changes a public interface):

Before any edit, search the codebase for callers of the function/symbol/yield shape you're changing. If your edit changes:

  • A function signature (add/remove/rename a parameter)
  • A return type or yield shape (e.g. yield process, gui_scriptyield process, gui_script, workspace_path)
  • A class hierarchy (add/remove a base class, change a method's name)
  • A module-level function name (rename)
  • A public attribute name

...you MUST update ALL callers in the same atomic commit. Use py_find_usages to locate them. If you change a contract and don't update callers, you have broken the codebase.

The whitespace-and-EOL rule (mandatory for set_file_slice):

The new_content must preserve:

  • The file's line ending convention (CRLF on Windows, LF on Linux — pick from the surrounding file, not from your text editor's default)
  • The indentation of the surrounding code (1 space per level, per conductor/code_styleguides/python.md §1)
  • The number of lines replaced (start_line..end_line must equal len(new_content.splitlines()))

If you mismatch any of these, the file will fail to parse. Run py_check_syntax and a real import after every set_file_slice.

9. No Diagnostic Noise in Production Code (Added 2026-06-09)

sys.stderr.write(f"[XYZ_DIAG] ...") lines added to src/*.py for debugging are technical debt the moment they ship. If you need to instrument for a one-time investigation:

  • Write the diag output to a log file: tests/artifacts/<test_name>.diag.log
  • Or to a standalone diagnostic script under /tmp/diag_<name>.py that imports the production module and exercises it
  • Or read the production source with get_file_slice and reason about it directly

Do NOT add diag lines to src/*.py "temporarily." If you must add them for a single test run, they 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.

Step-by-Step Workflow for gui_2.py

Check current state:

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 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 and EOL
  • Try again with exact match

If set_file_slice produces wrong indentation:

  • You wrote the wrong indent in new_content. The tool did what you asked.
  • Re-read the file with get_file_slice to confirm the surrounding indent
  • Rewrite the new_content with the correct indent
  • Do NOT use git checkout to "revert"

Alternative: Update Definition Approach

For large function rewrites, use py_update_definition:

name: function_name
path: src/gui_2.py
new_content: complete new function source

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:

    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:

    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:

    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/project_files.py - FileItem dataclass (moved out of src/models.py per module_taxonomy_refactor_20260627); src/models.py is now a ~1.5KB re-export shim
  • Context Composition function: line ~2748 (verify with py_get_code_outline before editing — line refs drift)

Test Command

uv run sloppy.py