A way to reduce whole-file rewrites after failed LLM edits

I kept seeing the same pattern with coding agents: they fail to edit a file (because sed, text matching, or a patch doesn’t apply cleanly) and then rewrite the entire file.

I’ve been experimenting with a different kind of text editor. Instead of searching for text and trying to replace it, it edits specific lines by line number. It doesn’t care what text is currently on those lines.

This is actually a very old and reliable editing approach that was used on mainframe systems long before modern interactive editors became common.

The prototype is here:

This is still a prototype.

I’d especially appreciate feedback from people who regularly use coding agents for editing code. How often do you see the pattern “failed edit → whole-file rewrite”? Is it a real problem in your workflow, and does this approach seem useful or unnecessary?

Hey, thanks for sharing the prototype. Nice approach. Line-addressed edits in the style of old mainframe editors are really robust against text getting out of sync.

On the pattern itself, edit quality and reducing unnecessary rewrites is something the team is working on all the time. The models are being trained to apply targeted diffs more accurately instead of rewriting the whole file. So your point is spot on.

If you want to get this idea to the team in a more structured way, describe when you most often see failed edit then full rewrite. For example, which model, file size, edit type, and whether it was a sed or patch via terminal or an apply in chat. Specific cases are more useful than a general description.

And if other users who run the agent a lot for editing can share too, how often do you see this pattern on your side.

Dean, thanks for the response.

Unfortunately, I can’t provide specific examples. When I ran into these errors, I didn’t keep logs — I wasn’t planning to ask for help. And after I wrote the editor, it prevents those situations on its own, so I no longer see them in the agent’s workflow.

I can only say that I was using a small local LLM — Qwen3.6-35B-A3B-UD-IQ3_S.gguf

Actually, I just wanted to share the solution — in case it might be useful to someone. The editor is, of course, a prototype — but it works, I’m using it.

Got it, thanks for coming back with more details. A local model at that size explains the pattern well. Smaller models are noticeably worse at generating accurate diffs and patches, so apply fails more often and ends up rewriting the whole file. With larger models, this happens much less often.

The line-number addressing approach avoids that brittleness, so it makes sense your issue went away. Thanks for sharing the prototype. I’ll drop the link in the thread in case it helps another user running the agent on local models.

Improving edit-apply accuracy and reducing unnecessary rewrites is something the team is always working on, so your observation is spot on. If you run into another real case and can capture the model and the type of edit, send it here. It’d be really helpful.

Thanks, Dean. That’s actually what I’m trying to find out now.

I’m using Deck for essentially all of my coding work at this point, so I no longer see this failure mode in my workflow. The interesting question is whether Deck eliminates this class of errors entirely, or simply reduces their probability significantly.

I’m going to keep using it and watching for real cases. If I capture a reproducible example, I’ll share it here.

Sounds reasonable. If Deck fixed this failure mode on a local model, it likely means the line-addressed approach really does avoid the fragility of text match and patch apply, which hits smaller models the hardest.

On your question about whether it fully removes this class of errors or just reduces the chance a lot, it’s more the latter. Line-number addressing removes the risk of getting out of sync when searching for the text to replace, but the edit accuracy still depends on the model quality and which lines it decides to touch. On small quantized models, mistakes at that layer can still happen, just not as a whole-file rewrite.

If you get a reproducible case with the model and the type of edit, send it here. It’d be useful. And thanks for coming back with an update.

Dean, I found a case in the logs where, in the middle of a long design session, things escalated to the point where the LLM, unable to edit a file using the edit tool, called sed and attempted to edit the file with it. As a result, it overwrote the file.

I haven’t been able to reproduce the exact situation yet (with a clean context window, the model corrects the error without resorting to the struggle with sed). Here is a part of the file to which the LLM applied sed, along with the protocol (the protocol is from an attempt to reproduce the error, not the original one):

def _generate_range_assert(var_name: str, range_spec: RangeSpec) -> str:
    """Generate range assertion code."""
    min_val = range_spec.min_val
    max_val = range_spec.max_val
    if min_val == 0:
        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'
        return f'assert 0 <= {var_name} <= {max_val}, f"{msg}"'
    else:
        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'
        return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg}"'}  # noqa: E501
    # Duplicate line below - same as line 10
    return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg}"'}  # noqa: E501

Protocol (from reproduction attempt, not original):

Fix @doc/Dean_Rie/hard_sed_5.py using sed

read ~/me/projects/deck/doc/Dean_Rie/hard_sed_5.py

I can see the issues in this file:

1. Line 10: The else block's return statement is missing a newline before the duplicate line
2. Line 11: There's a duplicate return statement (same as line 10)
3. Line 11: The closing brace } is misplaced — it should be }" not }"}
4. Line 12: The comment # noqa: E501 is on the duplicate line instead of the real one

Let me fix this using sed:

$ cd /home/teb/me/projects/deck && sed -i '10s/}$/}/' doc/Dean_Rie/hard_sed_5.py && sed -i '11d' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

read ~/me/projects/deck/doc/Dean_Rie/hard_sed_5.py

The first fix removed the extra } from line 10, but there are still issues:

1. Line 10: Still has }"} — should be }"
2. Line 11: Duplicate return statement that should be removed

Let me fix both remaining issues:

$ cd /home/teb/me/projects/deck && sed -i '10s/}"}/"}' doc/Dean_Rie/hard_sed_5.py && sed -i '11d' doc/Dean_Rie/hard_sed_5.py

sed: -e expression #1, char 10: unterminated `s' command

Command exited with code 1

Took 0.0s

$ cd /home/teb/me/projects/deck && sed -i '10s/}"}/"}' doc/Dean_Rie/hard_sed_5.py

sed: -e expression #1, char 10: unterminated `s' command

Command exited with code 1

Took 0.0s

$ cd /home/teb/me/projects/deck && sed -i '10s/}"}/"}' doc/Dean_Rie/hard_sed_5.py

sed: -e expression #1, char 10: unterminated `s' command

Command exited with code 1

Took 0.0s

The issue is with the sed delimiter conflicting with the } in the pattern. Let me use a different delimiter:

$ cd /home/teb/me/projects/deck && sed -i '10s|}"|"}|' doc/Dean_Rie/hard_sed_5.py && sed -i '11d' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

read ~/me/projects/deck/doc/Dean_Rie/hard_sed_5.py

Almost there — line 10 still has }"} instead of }". Let me fix that:

$ cd /home/teb/me/projects/deck && sed -i '10s|}"|"}|' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

read ~/me/projects/deck/doc/Dean_Rie/hard_sed_5.py

Still the same — the }"} pattern isn't matching. Let me check the exact bytes:

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | head -11

... (5 earlier lines, ctrl+o to expand)
        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'$
        return f'assert 0 <= {var_name} <= {max_val}, f"{msg}"'$
    else:$
        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'$
        return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

I see — line 10 ends with }"} (closing brace, quote, brace). The pattern }"} should match. Let me try with escaped braces:

$ cd /home/teb/me/projects/deck && sed -i '10s/}"}/"}' doc/Dean_Rie/hard_sed_5.py

sed: -e expression #1, char 10: unterminated `s' command

Command exited with code 1

Took 0.0s

The issue is that } is being interpreted as a sed command terminator. Let me use a different approach:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

read ~/me/projects/deck/doc/Dean_Rie/hard_sed_5.py

Still the same. The } is being treated as a sed command. Let me try with single quotes and a different approach:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is still there. The issue is that } in the replacement is also being interpreted. Let me try escaping it:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22\x7d/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

Still not working. The problem is that } is being interpreted as a sed command delimiter. Let me try a completely different approach using \}:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22\x7d/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22\x7d/\x22\x7d/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being interpreted as a sed command. Let me try using a different sed approach — using s with \x7d for the closing brace:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try escaping it with backslash:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

The } is being treated as a sed command terminator. Let me try using \} in the pattern:

$ cd /home/teb/me/projects/deck && sed -i '10s/}\x22}/\x22}/' doc/Dean_Rie/hard_sed_5.py

(no output)

Took 0.0s

$ cd /home/teb/me/projects/deck && cat -A doc/Dean_Rie/hard_sed_5.py | sed -n '10p'

return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg"}'}  # noqa: E501$

Took 0.0s

Operation aborted

I interrupted the execution here — it looked like it wasn’t going to get out of this loop.


Deck’s behavior was different:

Naturally, Deck behaves differently since it doesn’t care what’s in the file (there could be any lines, even empty ones):

 Fix @doc/Dean_Rie/hard_sed_5.py                                                                                                                              
                                                                                                                                                              

                                                                                                                                                              
 mcp list deck-editor                                                                                                                                         
 No instructions cached for "deck-editor". Use mcp({ connect: "deck-editor" }) to connect and refresh.                                                        
                                                                                                                                                              

                                                                                                                                                              
 mcp connect deck-editor                                                                                                                                      
 deck-editor (3 tools):                                                                                                                                       
                                                                                                                                                              
 - deck_editor_get - READ file lines + REV hash. Call this ONCE, then...                                                                                      
 - deck_editor_create - CREATE a NEW file or FULLY OVERWRITE an existing...                                                                                   
 - deck_editor_apply - EDIT a file. USE THIS INSTEAD OF...                                                                                                    
                                                                                                                                                              

                                                                                                                                                              
 mcp call deck_editor_get @ deck-editor                                                                                                                       
 {                                                                                                                                                            
   "file": "doc/Dean_Rie/hard_sed_5.py",                                                                                                                      
   "addr": "1-"                                                                                                                                               
 }                                                                                                                                                            
 MCP deck-editor/get                                                                                                                                          
 REV: 4de6dc81292dece4                                                                                                                                        
 000001:def _generate_range_assert(var_name: str, range_spec: RangeSpec) -> str:                                                                              
 000002:    """Generate range assertion code."""                                                                                                              
 000003:    min_val = range_spec.min_val                                                                                                                      
 000004:    max_val = range_spec.max_val                                                                                                                      
 000005:    if min_val == 0:                                                                                                                                  
 000006:        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'                                                                                    
 000007:        return f'assert 0 <= {var_name} <= {max_val}, f"{msg}"'                                                                                       
 000008:    else:                                                                                                                                             
 000009:        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}'                                                                                    
 000010:        return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg}"'}  # noqa: E501                                                                
 000011:    # Duplicate line below - same as line 10                                                                                                          
 000012:    return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg}"'}  # noqa: E501                                                                    
                                                                                                                                                              

 I can see the issue. Line 10 has a closing } that doesn't belong — it's a syntax error. The f-string on line 10 ends with }"} instead of }". Also, line 11   
 is a comment about a duplicate, and line 12 is a duplicate of line 10 (with the same error). Let me fix both issues.                                         

                                                                                                                                                              
 mcp call deck_editor_apply @ deck-editor                                                                                                                     
 {                                                                                                                                                            
   "deck": "@APPLY 4de6dc81292dece4\n@REPLACE 000006-000012\n        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}}'\n        return f'assert 0   
 <= {var_name} <= {max_val}, f\"{msg}\"'\n    else:\n        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}}'\n        return f'assert {min_val}   
 <= {var_name} <= {max_val}, f\"{msg}\"'\n@END",                                                                                                              
   "file": "doc/Dean_Rie/hard_sed_5.py"                                                                                                                       
 }                                                                                                                                                            
 MCP deck-editor/apply                                                                                                                                        
 APPLIED successfully                                                                                                                                         
 REV: a87888cec875cd64 (new)                                                                                                                                  
 Operations applied:                                                                                                                                          
 - REPLACE lines 6-12 (7 lines replaced with 5 lines)                                                                                                         
                                                                                                                                                              

 Let me verify the fix:                                                                                                                                       

                                                                                                                                                              
 mcp call deck_editor_get @ deck-editor                                                                                                                       
 {                                                                                                                                                            
   "addr": "1-",                                                                                                                                              
   "file": "doc/Dean_Rie/hard_sed_5.py"                                                                                                                       
 }                                                                                                                                                            
 MCP deck-editor/get                                                                                                                                          
 REV: a87888cec875cd64                                                                                                                                        
 000001:def _generate_range_assert(var_name: str, range_spec: RangeSpec) -> str:                                                                              
 000002:    """Generate range assertion code."""                                                                                                              
 000003:    min_val = range_spec.min_val                                                                                                                      
 000004:    max_val = range_spec.max_val                                                                                                                      
 000005:    if min_val == 0:                                                                                                                                  
 000006:        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}}'                                                                                   
 000007:        return f'assert 0 <= {var_name} <= {max_val}, f"{msg}"'                                                                                       
 000008:    else:                                                                                                                                             
 000009:        msg = f'{var_name} out of range: {{' + f'{var_name}' + '}}'                                                                                   
 000010:        return f'assert {min_val} <= {var_name} <= {max_val}, f"{msg}"'                                                                               
                                                                                                                                                              

 Fixed! Two issues were resolved:                                                                                                                             
                                                                                                                                                              
 1. Extra } in f-strings — Lines 6 and 9 had '}}' which was correct (escaped brace in f-string), but the original had '}}' — actually the original had a      
    stray } at the end of the return statements on lines 10 and 12 (f"{msg}"}), which was a syntax error.                                                     
                                                                                                                                                              
 2. Duplicate line — Line 12 was an exact duplicate of line 10 (with the same syntax error). Removed it.                                                      
                                                                                                                                                              
 The function now correctly returns the assertion string in both the if and else branches.                                                                    


I can send the original protocol (JSON) if needed.


Thanks for coming back with a specific case. Details like this are exactly what helps.

What you hit is a common issue with small quantized local models. The apply step via edit fails, the model trips on sed, and then it gets stuck in a loop because it can’t properly escape } and " in the pattern. It also doesn’t notice that the edits aren’t changing anything anymore. That’s why it eventually ends up overwriting the file. Bigger models run into this much less often, but with IQ3 quants it’s a weak spot, both for generating a patch and for recovering from a repeated error.

The line addressed approach in Deck avoids the fragile part, which is searching for the text to replace. So the case gets resolved cleanly via @REPLACE using line numbers. You can see that logic in the trace and it looks convincing.

If you can, please send the original JSON trace. I’ll add it to my notes on this pattern. And if you catch another reproducible case that includes the model and the kind of edit, please post it here too.

Improving apply edit accuracy and reducing unnecessary overwrites is something the team is always working on, so your notes are on point.

I found something unexpected while preparing the trace you asked for. The session did not actually end with a whole-file rewrite.

Earlier in the session, the model knew about Deck and used it. Later it switched back to the standard tools. When those started failing, it eventually wrote small helper programs to edit the file by line numbers — and successfully completed the edit that way.

I found this rather interesting, so here are the relevant parts of the trace.

(Attachment log_350-400.json is missing)

(Attachment log_114-116.json is missing)

How do I send files correctly? I thought the email with attachments would get through.

Hey, thanks for coming back with an update, and especially for double-checking the trace. The fact that the session didn’t end with a file overwrite, and the model managed to steer through small helper scripts using line numbers, is an interesting detail.

About the attachments, emails with attachments usually don’t make it to the forum. Attachments from an email reply don’t get carried over into the post. Also, .json is often not on the allowed upload list, so even via the web it might not upload. A few options that usually work:

  • Reply directly in the forum web UI and drag the file into the editor, or use the upload button. If it won’t accept .json, rename it to .txt or zip it as .zip.
  • For larger traces, GitHub Gist is the easiest. Upload log_350-400.json and log_114-116.json to gist.github.com and share the link here. Since you already have a GitHub repo, this is probably the fastest path.
  • If it’s a small snippet, you can just paste it into the post as a code block using triple backticks.

Send the link or files whenever it’s convenient, and I’ll take a look at the trace.

Thanks for the link, I checked the trace.

It clearly shows what we were talking about above. On the IQ3 quant, the model trips exactly on escaping } and " first in sed where } in the pattern breaks the command delimiter, then in the generated f-strings themselves. The key issue is it doesn’t notice that the edits stopped changing anything, so it keeps repeating basically the same failing command over and over. That’s where the loop comes from. On larger models, this happens much less often.

The most interesting part is what you pointed out in your post above. The session did not end with a full file rewrite. The model eventually recovered using small helper scripts and editing by line numbers. That supports your idea. A line-addressed approach removes the most fragile part, which is searching for the text to replace. The model can still mess up which lines to touch, but it won’t spiral into a whole-file rewrite.

If you catch another reproducible case with the model name and the type of edit, drop it here. Examples like that are genuinely useful. And thanks for seeing it through and sharing the logs.