From d7dc1e3b90f214f54832657d88229fbc8ced7f4c Mon Sep 17 00:00:00 2001 From: conductor-tier2 Date: Tue, 9 Jun 2026 14:02:41 -0400 Subject: [PATCH] docs(edit-workflow): fix set_file_slice rule + add contract-change check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- conductor/edit_workflow.md | 64 +++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/conductor/edit_workflow.md b/conductor/edit_workflow.md index bc22dc81..e19b7b44 100644 --- a/conductor/edit_workflow.md +++ b/conductor/edit_workflow.md @@ -16,16 +16,19 @@ The `manual-slop_edit_file` tool requires **exact string matches** (character-fo 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 -3. Get current state with get_file_slice +1. Run: py_check_syntax on src/.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 +- 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 +- 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) @@ -72,9 +75,47 @@ This keeps the `@property` attached to its original method. 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. Do Not Use `set_file_slice` For Multi-Line Content (Added 2026-06-07) +### 8. `set_file_slice` IS Valid for Multi-Line Content (Revised 2026-06-09) -`set_file_slice` does literal line replacement by design. It does not reindent, does not normalize EOL, does not parse decorators. Use it for surgical line-level edits (3-10 lines). If you need to insert or replace a multi-method block, use `manual-slop_edit_file` with verified exact-text old_string/new_string, or use `py_add_def` / `py_update_definition` for class/method-level work. +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_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_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/.diag.log` +- Or to a standalone diagnostic script under `/tmp/diag_.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 @@ -96,9 +137,16 @@ get_file_slice path=src/gui_2.py start_line=X end_line=Y - 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 +- 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`: