Three surgical fixes to conductor/edit_workflow.md:
1. **§2 "Verify Before Editing"** — removed the leftover
`git checkout -- src/gui_2.py` instruction. The user's
commit `4eba059e unfuck edit workflow` removed most of
the git checkout nuke instructions but missed §2. The
revised §2 now says: read the contract (function signature,
yield shape, return type) before editing, and DO NOT use
`git checkout` to revert. Ask the user.
2. **§3 "Reading Before Editing"** — added the line-number
offset check. `set_file_slice` uses 1-indexed inclusive
`start_line`/`end_line`; off-by-one is a common silent
failure. The rule is now: confirm the exact line range
with `get_file_slice` first.
3. **§8 "set_file_slice IS Valid for Multi-Line Content
(Revised 2026-06-09)"** — replaced the wrong rule
("Do not use set_file_slice for multi-line content") with
the correct rule: set_file_slice IS valid for 3-10 line
surgical edits, with a tool-selection guide (which tool
for which job), a mandatory contract-change check
(search for callers of the symbol being changed; update
all callers in the same atomic commit if the public
interface changes), and a mandatory whitespace-and-EOL
rule (preserve line ending, indentation, and line count).
4. **§9 "No Diagnostic Noise in Production Code
(Added 2026-06-09)"** — new section. Diag stderr goes
to log files or /tmp scripts, NOT src/*.py. If you must
add diag lines to production code, they are part of the
same atomic commit as the fix — they do not live
uncommitted in the working tree.
5. **"If set_file_slice produces wrong indentation"** —
new handler in the Step-by-Step Workflow. Tells the
agent: you wrote the wrong indent; the tool did what
you asked; re-read the file with get_file_slice; do
NOT use git checkout to revert.
These are the rule corrections the user demanded after
the Tier-2's bad set_file_slice + git nuke + diag-noise
behavior. Markdown only. No code modified.
8.5 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_sliceto 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 exactstart_lineandend_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_enablednew_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:
- Import the module:
python -c "from src.app_controller import AppController" - Instantiate the class
- Call the new method in the way it's expected to be called (
ctrl.foo_tsfor 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_slicefor surgical 3-10 line edits where you know the exact line range. Verify the line range withget_file_slicefirst. Thestart_lineandend_lineare 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_filefor exact-string replacement when you don't know the line range, or when the edit has a unique anchor string.py_update_definitionfor whole-function replacement (AST-detected).py_add_deffor adding a new method/class to a class.py_remove_deffor 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_script→yield 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_linemust equallen(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>.pythat imports the production module and exercises it - Or read the production source with
get_file_sliceand 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:
- Make the smallest possible change (3-10 lines)
- Run
py_check_syntaxto verify - If syntax error, immediately report to the user to address.
- 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_sliceto 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_sliceto confirm the surrounding indent - Rewrite the
new_contentwith the correct indent - Do NOT use
git checkoutto "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:
-
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 -
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() -
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() -
Fixed presets bar with push_item_width(150) on the combo
-
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
uv run sloppy.py